Java并发 – 中断策略

huangapple go评论125阅读模式
英文:

Java Concurrency - Interruption Policies

问题

我正在阅读《Java Concurrency in Practice》。在第Cancellation and Shutdown章节的中断政策一节中提到:

除非任务被明确设计为在具有特定中断策略的服务内运行,否则任务不应假设其执行线程的中断策略。无论任务将中断解释为取消操作还是在中断时采取其他操作,它都应当注意保留执行线程的中断状态。如果任务不打算将InterruptedException传播给其调用者,在捕获InterruptedException后应当恢复中断状态:
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() + " producer中的中断状态:" + Thread.currentThread().isInterrupted());
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted()) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException e) {
            /* 允许线程退出 */
            Thread.currentThread().interrupt();
            System.out.println(Thread.currentThread().getName() + " producer中捕获的中断状态:" + 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() + " main中的中断状态:" + generator.isInterrupted());
    }

    // 进行一些操作
    private static void consume(BigInteger take) {
        System.out.println(take);
    }

    private static int counter = 1;

    private static boolean needMorePrimes() {
        counter++;
        if(counter == 10){
            // 当counter达到10时返回false
            return false;
        }
        return true; 
    }

输出:

// 当主类中的 TimeUnit.SECONDS.sleep(5); 没有被注释时

Thread-0 producer中的中断状态false
2
3
5
7
11
13
17
19
Thread-0 producer中捕获的中断状态true
Thread-0 main中的中断状态false
// 当主类中的 TimeUnit.SECONDS.sleep(5); 被注释时
Thread-0 producer中的中断状态false
2
3
5
7
11
13
17
19
Thread-0 main中的中断状态true
Thread-0 producer中捕获的中断状态true

问题:

  1. 只需在主线程的主类中添加 TimeUnit.SECONDS.sleep(5) 方法。执行线程(即generator)的中断状态就会被重置。如果我注释掉 TimeUnit.SECONDS.sleep(5) 方法,那么中断状态将被保留。为什么会发生这种情况,原因是什么?

  2. 在书中提到,线程只应被其所有者中断。在上面的示例中,谁是所有者?我认为是主方法线程。

英文:

I am reading Java Concurrency in Practice
. In section Interruption Policies in chapter

Cancellation and Shutdown

Its mentioned

>A task should not assume anything about the interruption policy of its executing thread unless it is explicitly designed to run within a service that has a specific interruption policy. Whether a task interprets interruption as cancellation or takes some other action on interruption, it should take care to preserve the executing thread's interruption status. If its not going to propagate InterruptedException to its caller, it should restore the interruption status after catching InterruptionException:
Thread.currentThread().interrupt()

So I tried to play around with listing sample to understand. But I am confused with the output.

PrimeProducer

public class CorrectPrimeProducer extends Thread {

    private final BlockingQueue&lt;BigInteger&gt; queue;

    public CorrectPrimeProducer(BlockingQueue&lt;BigInteger&gt; queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName()+&quot; interrupt status in producer:&quot; + 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()+&quot; interrupt status in producer catch:&quot; + Thread.currentThread().isInterrupted());
        }
    }
}

main method##

public static void main(String[] args) throws InterruptedException {
        BlockingQueue&lt;BigInteger&gt; primes = new LinkedBlockingQueue&lt;&gt;();
        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()+&quot; interrupt status in main:&quot;+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; 
    }

Output:

// 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

Question

  1. Just by adding TimeUnit.SECONDS.sleep(5) in main thread in main class. The executing thread (ie, generator) interrupt status is getting reset. If I comment the TimeUnit.SECONDS.sleep(5) method then in that case interrupt status is retained. Why is this happening and how ?

  2. In book it's mentioned A thread should be interrupted only by its owner . Here in the above example who is the owner ? I think its main method thread.

答案1

得分: 3

Just by adding TimeUnit.SECONDS.sleep(5) in main thread in main class. The executing thread (ie, generator) interrupt status is getting reset. If I comment the TimeUnit.SECONDS.sleep(5) method then in that case interrupt status is retained. Why is this happening and how?

You are not using any synchronization mechanism (apart from blocking queue) between the main thread and the CorrectPrimeProducer so when the main thread prints the status - the CorrectPrimeProducer may not have preserved the interrupted status yet (by performing catch block instructions) thus you get false as the result.

When you add sleep to the main Thread you just increase the possibility that the CorrectPrimeProducer thread preserves the interruption status by invoking catch block instructions before the main thread tries to print its status. That is why it prints true.

In book it's mentioned A thread should be interrupted only by its owner. Here in the above example who is the owner? I think it's the main method thread.

In this case you are the owner (the owner is the code that creates the thread) of the CorrectPrimeProducer thread so you decide what interruption means to it. For example, you could recreate it if it was interrupted (this happens, for example, for Threads from Java thread pools by default).

英文:

> Just by adding TimeUnit.SECONDS.sleep(5) in main thread in main class. The executing thread (ie, generator) interrupt status is getting reset. If I comment the TimeUnit.SECONDS.sleep(5) method then in that case interrupt status is retained. Why is this happening and how ?

You are not using any synchronization mechanism (apart from blocking queue) between the main thread and the CorrectPrimeProducer so when the main thread prints the status - the CorrectPrimeProducer may not have preserved the interrupted status yet (by performing catch block instructions) thus you get false as the result.

When you add sleep to the main Thread you just increase the possibility that the CorrectPrimeProducer thread preserves the interruption status by invoking catch block instructions before the main thread tries to print it's status. That is why it prints true.

> In book it's mentioned A thread should be interrupted only by its owner . Here in the above example who is the owner ? I think its main method thread.

In this case you are the owner (the owner is the code that creates the thread) of the CorrectPrimeProducer thread so you decide what interruption means to it. For example you could recreate it if it was interrupted (this happens for example for Threads from java thread pools by default).

答案2

得分: 1

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

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

虽然这在规范中没有记录,但这就是发生的情况。例如,参见 这个错误报告

> 这里没有违反规范,所以我将其作为增强请求而不是错误报告。可以说,缺乏规范是一个错误 - 我们确实有意规定了“终止后的中断不会产生影响”,以应对这样一个事实,即中断状态存储在虚拟机中,一旦线程终止就不再存在。然而,我们忽略了在 Thread.isInterrupted 规范中反映这一点。

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

英文:

By adding TimeUnit.SECONDS.sleep(5) you are giving enough time for the thread to terminate.

When a thread terminates, its interrupt flag is cleared.

This is not documented in the specification, but it's what happens. See for example this bug report:

> There is no specification being violated here so I've made this an enhancement request rather than a bug. Arguably the lack of specification is a bug - we did intentionally specify that "interrupt after termination need have no affect" to deal with the fact that the interrupt state is stored in the VM and no longer exists once a thread has terminated. However we neglected to reflect that in the Thread.isInterrupted spec.

Without the extra sleep, I suspect that in theory you could see both true and false interrupt status because there's a race condition, but it's far more likely you will see true thanks to thread scheduling. The time window where the interrupt status is false, between the exception being thrown and it the interrupt status being restored in the catch block, is incredibly small.

huangapple
  • 本文由 发表于 2020年8月26日 04:34:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/63586688.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定