我正在阅读有关在https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html中结束帖子的方法,这是我在问题How do you kill a thread in Java?中找到的链接
在第一个链接中,他们首先讨论使用volatile
变量向您要终止的线程发信号。如果变量具有表示停止的值(例如,如果它是null
),则该线程应该检查该变量并停止操作。因此,要终止该线程,您可以将该变量设置为null
。
然后他们讨论添加中断以帮助长时间阻塞的线程。他们给出了一个stop()方法的示例,该方法将volatile变量(waiter)设置为null,然后再抛出一个中断。
public void stop() {
Thread moribund = waiter;
waiter = null;
moribund.interrupt();
}
我只是想知道,为什么你们两个都需要?为什么不只使用interrupt(),然后正确处理它?这对我来说似乎是多余的。
答案 0 :(得分:3)
(总的来说,第一部分,可以说我没有注意到问题的具体细节。跳到最后讨论问题中讨论的技术规范的部分。)
没有好的技术原因。这部分是关于人为限制,部分是关于api设计的混乱。
首先考虑应用程序开发人员的优先级是创建解决业务问题的工作代码。彻底学习像这样的低水平api会在急于完成工作时迷失方向。
第二,当你学习东西以达到足够好的状态并将其留在那里时,就会出现这种趋势。线程和异常处理之类的东西都是被忽视的背景主题。中断是线程书籍的书籍主题的背面。
现在假设一个代码库由多个具有不同技能水平和注重细节的人工作,他们可能会忘记从等待和睡眠中抛出InterruptedException重置中断标志,或者中断与isInterrupted不同,或者InterruptedIoException也重置中断标志。如果你有一个抓住IOException的finally块,你可能会错过InterruptedException是IOException的子类,你可能会错过恢复中断标志。可能人们匆忙决定下地狱,我不能指望这个被打断的旗帜
是不是?没有。
手动滚动标志无助于短路等待或中断的方式。
Java 5并发工具希望任务使用中断进行取消。执行者期望提交给他们的任务检查中断以便优雅地退出。您的任务可能使用其他组件,例如阻塞队列。该队列需要能够响应中断,使用它的东西需要知道中断。手动标志不起作用,因为java 5类无法知道它。
因为你正在使用期望它的工具而不能使用中断,但由于无法管理的技术性而对标志值没有信心,所以会导致这种代码。
(咆哮结束,现在实际上回应了特定的techspec示例)
好的,特别是看这篇技术指南文章。有三点突出:
1)除了缩短睡眠时间外,它根本没有使用中断。然后它只是压制异常,并没有打扰恢复标志或检查它。 可以使用InterruptedException通过在while循环之外捕获它来终止,但这并不是这样做的。这似乎是一种奇怪的方式来做到这一点。2)随着示例的充实,很明显正在使用标志来打开和关闭等待。有人可能会使用中断,但这不是惯用语。所以在这里有一面旗帜是可以的。
3)这是技术指南中的玩具示例。并非所有Oracle内容都与techspecs或API文档一样具有权威性。有些教程有错误陈述或不完整。这可能是编写代码的原因,因为作者认为读者不熟悉中断的工作方式,最好尽量减少其使用。众所周知,技术作家会做出类似的选择。
如果我重写这个以使用中断,我仍然会保留旗帜;我使用中断来终止,并使用该标志来暂停/恢复功能。
答案 1 :(得分:1)
请参阅此documentation。
您的主题应检查thread.isInterrupted()
状态,例如:
Thread myThread = new Thread() {
@Override
public void run() {
while (!this.isInterrupted()) {
System.out.println("I'm working");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//We would like also exit from the thread
return;
}
}
}
};
当你想要停止线程时,你应该调用
myThread.interrupt();
此外,我们可以使用静态方法Thread.interrupted()
来检查状态,但之后,方法会清除它,您必须再次调用myThread.interrupt()
再次设置状态。但我不建议使用Thread.interrupted()
方法。
这种方法有助于优雅地停止线程。
所以我也没有看到任何使用额外volatile
变量的理由。
可以通过@ lexicore的答案中的附加volatile
标记来达到相同的行为,但我认为它是冗余代码。
答案 2 :(得分:0)
@NathanHughes是完全正确的,但我要将他的长答案改写为几句话:第三方代码。
这不仅仅是关于愚蠢的初级开发者", - 在应用程序的某些时候,你将使用很多的第三方库。这些库不会绅士尊重您对并发性的假设。有时他们会默默地重置中断标志。使用单独的标志解决了这个问题。
不,您无法摆脱第三方代码并将其称为一天。
假设您正在编写库,例如,ThreadPoolExecutor。库中的一些代码需要处理中断......甚至无条件地重置中断标志。为什么?因为之前的Runnable已经完成,并且正在运行新的Runnable。不幸的是,在这一点上可能会有一个陈旧的中断标志,目的是......等等,谁再次出现呢?它可以用于之前的Runnable或新的(尚未运行)Runnable, - 无法分辨。这就是你将isCancelled
方法添加到FutureTask的原因。并在执行新的Runnable之前无条件地重置中断标志。听起来很熟悉?
Thread#interrupt
与在该线程上运行的实际工作单元完全分离,因此需要添加其他标志。一旦你开始这样做,你必须一直这样做 - 直到最外层和最里面的工作单位。实际上,在线程上运行未知的用户提供的代码会导致Thread#interrupted
不可靠(即使Thread#interrupt
仍能正常工作!)