【问题标题】:Java Concurrency - Interruption PoliciesJava 并发 - 中断策略
【发布时间】:2020-12-14 14:35:17
【问题描述】:

我正在阅读Java Concurrency in Practice 。在章节中的中断策略部分

取消和关闭

提到

除非任务明确设计为在具有特定中断策略的服务中运行,否则不应对其执行线程的中断策略进行任何假设。无论任务将中断解释为取消还是对中断采取其他操作,它都应该注意保留执行线程的中断状态。如果它不打算将 InterruptedException 传播给它的调用者,它应该在捕获 InterruptionException 后恢复中断状态: Thread.currentThread().interrupt()

所以我尝试使用列表示例来理解。但我对输出感到困惑。

PrimeProducer

public class CorrectPrimeProducer extends Thread {

    private final BlockingQueue<BigInteger> queue;

    public CorrectPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName()+" interrupt status in producer:" + Thread.currentThread().isInterrupted());
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted()) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException e) {
            /* Allow thread to exit */
            Thread.currentThread().interrupt();
            System.out.println(Thread.currentThread().getName()+" interrupt status in producer catch:" + Thread.currentThread().isInterrupted());
        }
    }
}

主要方法##

public static void main(String[] args) throws InterruptedException {
        BlockingQueue<BigInteger> primes = new LinkedBlockingQueue<>();
        CorrectPrimeProducer generator = new CorrectPrimeProducer(primes);
        generator.start();
        try {
            while (needMorePrimes()) {
                consume(primes.take());
            }
        } finally {
            generator.interrupt();
        }
        TimeUnit.SECONDS.sleep(5);
        System.out.println(generator.getName()+" interrupt status in main:"+generator.isInterrupted());
    }

    //do something
    private static void consume(BigInteger take) {
        System.out.println(take);
    }

    private static int counter = 1;

    private static boolean needMorePrimes() {
        counter++;
        if(counter == 10){
// after counter reaches 10 return false
            return false;
        }
        return true; 
    }

输出:

// when TimeUnit.SECONDS.sleep(5); in main class is not commented

Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in producer catch:true
Thread-0 interrupt status in main:false
//When TimeUnit.SECONDS.sleep(5); in main class is commented
Thread-0 interrupt status in producer:false
2
3
5
7
11
13
17
19
Thread-0 interrupt status in main:true
Thread-0 interrupt status in producer catch:true

问题

  1. 只需在主类的主线程中添加 TimeUnit.SECONDS.sleep(5) 即可。正在执行的线程(即生成器)中断状态正在重置。如果我评论 TimeUnit.SECONDS.sleep(5) 方法,那么在这种情况下会保留中断状态。为什么会发生这种情况以及如何发生?

  2. 在书中提到一个线程应该只被它的所有者打断。在上面的例子中,谁是所有者?我认为它的主要方法线程。

【问题讨论】:

  • Re,“任务不应该假设任何关于中断策略的事情......”是的,不应该。但是,如果您的代码调用任何库代码,那么您怎么知道库作者确实没有假设中断意味着整个事物正在关闭,并且他们可以放弃正在进行的工作,而returnthrow 无需清理?您知道,除非他们明确记录了他们的库如何处理中断(这几乎从来没有),或者您自己与作者交谈。我对将中断用于除关闭进程之外的任何其他目的感到非常不安。

标签: java multithreading concurrency java.util.concurrent


【解决方案1】:

通过添加TimeUnit.SECONDS.sleep(5),您为线程提供了足够的时间终止。

当一个线程终止时,它的中断标志被清除。

规范中没有记录,但这就是发生的事情。例如见this bug report:

这里没有违反规范,所以我将此作为增强请求而不是错误。可以说,缺乏规范是一个错误——我们确实故意指定“终止后的中断不需要影响”以处理中断状态存储在 VM 中并且一旦线程终止就不再存在的事实。但是我们忽略了在 Thread.isInterrupted 规范中反映这一点。

如果没有额外的sleep,我怀疑理论上您可以同时看到truefalse 中断状态,因为存在竞争条件,但由于线程调度,您更有可能看到true。中断状态为假的时间窗口,在抛出异常和在 catch 块中恢复中断状态之间的时间窗口非常小。

【讨论】:

    【解决方案2】:

    只需在主类的主线程中添加 TimeUnit.SECONDS.sleep(5) 即可。正在执行的线程(即生成器)中断状态正在重置。如果我评论 TimeUnit.SECONDS.sleep(5) 方法,那么在这种情况下会保留中断状态。为什么会发生这种情况以及如何发生?

    您没有在主线程和CorrectPrimeProducer 之间使用任何同步机制(除了阻塞队列),因此当主线程打印状态时 - CorrectPrimeProducer 可能尚未保留中断状态(通过执行 @ 987654323@ 块指令)因此你得到false 作为结果。

    当您将sleep 添加到主Thread 时,您只会增加CorrectPrimeProducer 线程通过在主线程尝试打印其状态之前调用catch 块指令来保留中断状态的可能性。这就是它打印true的原因。

    在书中提到一个线程应该只被它的所有者打断。在上面的例子中,谁是所有者?我认为它的主要方法线程。

    在这种情况下,您是CorrectPrimeProducer 线程的所有者(所有者是创建线程的代码),因此您决定中断对它意味着什么。例如,如果它被中断,您可以重新创建它(例如,默认情况下来自 java 线程池的Threads 会发生这种情况)。

    【讨论】:

    • 谢谢。但是如果使用睡眠,则主线程将 CorrectPrimeProducer 线程的中断状态打印为 false。
    猜你喜欢
    • 1970-01-01
    • 2011-01-05
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2018-09-08
    相关资源
    最近更新 更多