英文:
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
问题:
-
只需在主线程的主类中添加 TimeUnit.SECONDS.sleep(5) 方法。执行线程(即generator)的中断状态就会被重置。如果我注释掉 TimeUnit.SECONDS.sleep(5) 方法,那么中断状态将被保留。为什么会发生这种情况,原因是什么?
-
在书中提到,线程只应被其所有者中断。在上面的示例中,谁是所有者?我认为是主方法线程。
英文:
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<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());
}
}
}
main method##
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;
}
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
-
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 ?
-
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 - theCorrectPrimeProducer
may not have preserved the interrupted status yet (by performingcatch
block instructions) thus you getfalse
as the result.When you add
sleep
to the mainThread
you just increase the possibility that theCorrectPrimeProducer
thread preserves the interruption status by invokingcatch
block instructions before the main thread tries to print its status. That is why it printstrue
.
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, forThread
s 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 Thread
s from java thread pools by default).
答案2
得分: 1
通过添加 TimeUnit.SECONDS.sleep(5)
,您为线程提供了足够的时间来终止。
当线程终止时,它的中断标志会被清除。
虽然这在规范中没有记录,但这就是发生的情况。例如,参见 这个错误报告:
> 这里没有违反规范,所以我将其作为增强请求而不是错误报告。可以说,缺乏规范是一个错误 - 我们确实有意规定了“终止后的中断不会产生影响”,以应对这样一个事实,即中断状态存储在虚拟机中,一旦线程终止就不再存在。然而,我们忽略了在 Thread.isInterrupted 规范中反映这一点。
如果没有额外的 sleep
,我怀疑在理论上您可能会同时看到 true
和 false
的中断状态,因为存在竞争条件,但由于线程调度的原因,更有可能看到 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论