Java并发-中断政策

问题描述

我正在阅读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. 在书中提到线程仅应由其所有者中断。在上面的示例中,谁是所有者?我认为它的主要方法是线程。

解决方法

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

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

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

在书中提到线程仅应由其所有者中断。在上面的示例中,谁是所有者?我认为它的主要方法是线程。

在这种情况下,您是CorrectPrimeProducer线程的所有者(所有者是创建线程的代码),因此您可以决定中断的含义。例如,如果它被中断,您可以重新创建它(例如,默认情况下,来自Java线程池的Thread s会发生这种情况。)

,

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

当线程终止时,其中断标志被清除。

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

这里没有违反任何规范,因此我提出了增强请求而不是错误。可以说,缺乏规范是一个错误-我们确实有意指定“终止后的中断无需影响”,以处理中断状态存储在VM中且线程终止后不再存在的事实。但是,我们忽略了在Thread.isInterrupted规范中反映这一点。

在没有多余的sleep的情况下,我怀疑理论上您可以同时看到truefalse的中断状态,因为存在竞争状况,但是您看到{{ 1}}得益于线程调度。在抛出异常与在catch块中恢复中断状态之间,中断状态为假的时间窗口非常小。