【问题标题】:Java Thread interrupt only wait, join and sleepJava 线程中断仅等待、加入和睡眠
【发布时间】:2020-02-11 07:19:20
【问题描述】:

Thread.interrupt():

中断这个线程。除非当前线程 正在中断自己,这始终是允许的,checkAccess 调用此线程的方法,可能会导致 SecurityException 被扔掉。

如果该线程在调用 wait()、wait(long) 时被阻塞, 或 Object 类或 join() 的 wait(long, int) 方法, join(long)、join(long, int)、sleep(long) 或 sleep(long, int)、方法 这个类,那么它的中断状态将被清除,它会 收到一个 InterruptedException。

如果此线程在 I/O 操作中被阻塞 InterruptibleChannel 则通道将被关闭,线程的 中断状态将被设置,线程将收到一个 ClosedByInterruptException。

如果该线程在 Selector 中被阻塞,则该线程的中断 状态将被设置,它将立即从选择中返回 操作,可能具有非零值,就像选择器的 唤醒方法被调用。

如果前面的条件都不成立,那么这个线程的中断 状态将被设置。

中断一个不活跃的线程不需要有任何效果。

假设我们有这个代码:

AtomicBoolean thread1Done = new AtomicBoolean(false);

//write in file
Thread thread1 = new Thread(() -> {

    try(var writer = Files.newBufferedWriter(Paths.get("foo.txt"))){

        for(int i = 0; i < 10000; i++){
            writer.write(i);
            writer.newLine();
        }

    }catch(Exception e){ e.printStackTrace(); }

    thread1Done.set(true);

});

//interrupt thread1
Thread thread2 = new Thread(() -> {

    while(!thread1Done.get()){
        thread1.interrupt();
    }

});

thread2.start();
thread1.start();

thread1 永远不会在文件中写入任何内容,因为来自thread2thread1.interrupt()

writer.newLine(); 处总是以java.nio.channels.ClosedByInterruptException 结尾,而foo.txt 为空。

有没有办法只打断wait, join and sleep,而忽略其余的?

我在 Windows10 x64 上使用 JDK10 运行我的代码。

【问题讨论】:

  • 当我运行你的代码时,我假设我得到了同样的异常,不像@diginoise。默认情况下,它似乎使用可中断的通道。但是,根据您的问题,我假设您希望此 I/O 读取部分(或者这只是其他示例?)是不间断的?
  • 最简单的解决方案是不使用 java.nio 包中的类,正如 answer 所示。
  • 直接使用BufferedWriter,而不是通过Files

标签: java multithreading


【解决方案1】:

就目前而言,您的代码运行,以便Thread 1 完成将 10k 行写入输出文本文件,换句话说,Thread 2 中断,但Thread 1 中没有语句是可中断。这是因为(我想)BufferedWriter 使用 uninterruptible I/O 打开文件。

如果您希望Thread 1 中的长循环可中断,您可以在长循环中添加以下检查:

for(int i = 0; i < 10000; i++){
    if (Thread.currentThread().isInterrupted()) {    //interruptible loop
        break;
    }
    writer.write(i);
    writer.newLine();
    System.out.println(i);
}

然后通过将Thread 2 的中断延迟 10 毫秒,我得到只有几百个条目被写入文件(没有延迟,它会立即被中断)。

当我将Thread 1 换成使用可中断通道
(就像FileChannel extends AbstractInterruptibleChannel 一样):

Thread thread1 = new Thread(() -> {
    FileChannel fc = null;
    try ( 
       FileChannel fc = FileChannel.open(Paths.get("foo.txt"), 
                        StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    )
    {
       fc = FileChannel.open(Paths.get("foo.txt"), 
          StandardOpenOption.CREATE, StandardOpenOption.WRITE
       );

       for(int i = 0; i < 10000; i++){
           fc.write(ByteBuffer.wrap(("" + i).getBytes()));
           fc.write(ByteBuffer.wrap(("\n").getBytes()));
           System.out.println(i);
       }
    } catch (Exception e) {
       e.printStackTrace();
    } 
}

...我确实得到了可中断的文件写入线程。

【讨论】:

  • As it stands your code runs so that Thread 1 finishes writing 10k lines into the output text file, in other words Thread 2 interrupts, but there are no statements in Thread 1 which are interruptible. This is because (I suppose) BufferedWriter opens the file using uninterruptible I/O . 你运行了我的代码?它总是在writer.newLine()ClosedByInterruptException 处被打断,我不想发生这种情况。我想打断只等待、睡眠和加入
  • 我确实运行了它,整个文件都写好了。我刚刚使用JDK8和JDK11进行了测试。你是在 windows、linux 还是 mac 上运行它?
  • 带有 jdk10 的 Windows
  • 这很奇怪。我在 Windows 10 上运行,整个过程就完成了。您的异常表明您的 I/O 是可中断的。我的好像不是。
  • 如果您不想中断写作,而只是等待,应该有其他方法可以解决您的问题。线程 1 可以发出写入完成的信号,然后线程 2 可以尝试中断。
【解决方案2】:

如果你想要的是只在线程被等待、加入和睡眠调用而不是在 IO 操作上阻塞时才中断线程,你可以在调用中断方法之前简单地检查线程状态。您可以参考下面链接中的api和不同的状态。

https://docs.oracle.com/javase/10/docs/api/java/lang/Thread.State.html

示例代码可能如下所示。

while ( ( thread1.getState() == Thread.State.WAITING || thread1.getState() == Thread.State.TIMED_WAITING ) && !thread1Done.get()) {
    thread1.interrupt();
}

【讨论】:

    猜你喜欢
    • 2017-07-02
    • 2012-12-22
    • 1970-01-01
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    相关资源
    最近更新 更多