Java 线程 InterruptExceptioon 处理仅在某些情况下停止执行更多代码

Java thread InterruptExceptioon handling stopped further code from being executed only in some situation

以下简短示例代码说明了我无法解释的行为

    ConcurrentLinkedDeque<Double> testQueue = new ConcurrentLinkedDeque<>();
    
    Thread t = new Thread(() -> {
        long cnt = 0;
        try {
            while(!Thread.currentThread().isInterrupted()) {
                testQueue.add(Math.random());
                cnt++;
                try {
                    Thread.sleep(100);
                }catch (Exception e) {
                    System.out.println(String.format("inner interrupted inserted %d record", cnt));
                }
            }
        } catch (Exception e) {
            System.out.println(String.format("interrupted inserted %d record", cnt));
        }
        System.out.println(String.format("inserted %d record", cnt));
    });

如果我调用t.interrupt()来中断线程

预期的行为是 2 行打印内容:

inner interrupted inserted %d record
inserted %d record

其中 %d 被替换为相应的值。

但实际结果是

inner interrupted inserted %d record

如果我们删除线程的内部捕获

    ConcurrentLinkedDeque<Double> testQueue = new ConcurrentLinkedDeque<>();
    
    Thread t = new Thread(() -> {
        long cnt = 0;
        try {
            while(!Thread.currentThread().isInterrupted()) {
                testQueue.add(Math.random());
                cnt++;
                Thread.sleep(100);
            }
        } catch (Exception e) {
            System.out.println(String.format("interrupted inserted %d record", cnt));
        }
        System.out.println(String.format("inserted %d record", cnt));
    });

然后我们得到输出

interrupted inserted %d record
inserted %d record

正如预期的那样,现在外层捕获正在捕获 InterruptException

但是,如果我将 break 放在第一个代码示例的最内层 catch 中,如下所示:

    ConcurrentLinkedDeque<Double> testQueue = new ConcurrentLinkedDeque<>();
    
    Thread t = new Thread(() -> {
        long cnt = 0;
        try {
            while(!Thread.currentThread().isInterrupted()) {
                testQueue.add(Math.random());
                cnt++;
                try {
                    Thread.sleep(100);
                }catch (Exception e) {
                    System.out.println(String.format("inner interrupted inserted %d record", cnt));
                    break; //this one
                }
            }
        } catch (Exception e) {
            System.out.println(String.format("interrupted inserted %d record", cnt));
        }
        System.out.println(String.format("inserted %d record", cnt));
    });

产生了预期的行为。

为什么 catch 块的放置会产生这样的差异?

请参阅 Thread#interrupt 的文档:

If this thread is blocked in an invocation of [...] sleep(long), or sleep(long, int), [...], then its interrupt status will be cleared and it will receive an InterruptedException.

If none of the previous conditions hold then this thread's interrupt status will be set.

这意味着在您的示例中 Thread#sleep 抛出异常 XOR Thread.currentThread().isInterrupted() 给您 true。

因此,在您的第一个示例中,您捕获了异常,在无限循环中执行打印和 enter/stay,因为 Thread.currentThread().isInterrupted() 始终是 false

在你的第二个例子中,try-catch 没有嵌套在循环中,所以两个打印都发生并且线程按预期终止。

在您的第三个示例中,您只是显式地退出了循环。


仅供参考: 你不需要写 System.out.println(String.format(...))。有System.out.printf,但别忘了\n.