我在玩以下示例:
public static void interrupted() throws InterruptedException {
Runnable task = () -> {
while(true && !Thread.currentThread().isInterrupted()) {
System.out.println("task executing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread thread = new Thread(task);
thread.start();
//lets allow spawned thread to run by sleeping main thread for 10 seconds
Thread.currentThread().sleep(10000);
thread.interrupt();
}
当我运行此方法时,我期望的是:
1通过Runnable启动的任务将运行几次并打印SOP
由于主线程休眠10秒,因此2可能是
3一旦主线程唤醒,它就会在生成的线程上调用中断
4我们正在检查isInterrupted的Runnable任务中,因此应该触发该事件,并应退出生成的线程,从而允许JVM退出。
但是这没有发生,并且eclipse / jvm显示生成的线程仍在运行while循环
这是控制台:
task executing
task executing
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at basics.StopEx.lambda$1(StopEx.java:39)
at java.lang.Thread.run(Thread.java:748)
task executing
task executing
task executing
task executing
........
有关中断的Java文档似乎确实表明,调用“睡眠”会导致不同的行为(下面突出显示)-有人可以对这里到底发生了什么以及为什么发生了一些看法吗?
中断该线程。
除非当前线程一直在中断自身(始终允许),否则将调用此线程的checkAccess方法,这可能会引发SecurityException。
如果调用Object类的wait(),wait(long)或wait(long,int)方法或join(),join(long),此类的join(long,int),sleep(long)或sleep(long,int)方法,则其中断状态将被清除,并将收到InterruptedException。
如果此线程在InterruptibleChannel上的I / O操作中被阻塞,则该通道将被关闭,该线程的中断状态将被设置,并且该线程将收到java.nio.channels.ClosedByInterruptException。
如果此线程在java.nio.channels.Selector中被阻塞,则将设置该线程的中断状态,并且它将立即从选择操作中返回,可能具有非零值,就像选择器的唤醒方法一样被调用。
如果以上条件均不成立,则将设置该线程的中断状态。
中断未运行的线程不会产生任何效果。
答案 0 :(得分:2)
这是因为中断标志如何用于线程。
基本上,当您中断线程时,只会发生一件事:设置中断标志,仅此而已。然后由代码决定该线程是否真正起作用。
通常的想法是,如果可以的话,您放弃正在做的事情,并抛出一些明智的异常。
java guarantee 中内置的一些方法可以确保它们确实起作用。您可以轻松识别它们:它们都声明为抛出InterruptedException
。有许多方法是故意未定义中断时行为的(取决于操作系统):所有阻止I / O方法的行为都是这样。如果您处于阻塞的网络读取中,并且中断了读取时被阻塞的线程,则可能导致读取调用上出现IOException,否则。
更重要的是,当“处理”中断(并且中止+引发异常正在处理它!)时,标志已清除。
请注意,如果您使用标准的检查方式Thread.interrupted()
,则检查该标志已将其清除。此方法检查您自己线程的中断标志并将其清除。请使用以下示例:您已经编写了if
语句或不处理中断的事实,这意味着您已处理了该中断。
因此,如果您只想响应中断就返回,则正确的代码是:
Runnable task = () -> {
while (!Thread.interrupted()) {
System.out.println("Executing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
return;
}
}
};
无论中断何时发生,此方法都将退出。在99.99%的情况下,它会在阻止睡眠的同时发生。然后,该标志将为false,并引发异常。在不太可能的情况下,它是在外部处理时发生的,而while条件会将其拾取。
请注意,如果引发了中断标志,并且调用了Thread.sleep
之类的方法,则该方法将通过清除该标志并抛出InterruptedException
立即 完成执行。不管在哪里发生中断,上面的示例都将执行相同的操作(静默退出run方法,并清除中断标志)。
答案 1 :(得分:2)
InterruptedException
和Thread.isInterrupted()
实际上是非常独立的。当您在长时间运行的循环中使用sleep()
时,就引入了复杂性。您的sleep(5000)
实际上是清除isInterrupted
标志并抛出InterruptedException
的那个人。
如果尝试以下操作,则会发现InterruptException
线程时实际上没有抛出任何interrupt()
。
@Override public void run() {
int b = 0;
while (true) {
if (b++ == 0) System.out.println("running...");
}
}
课程是,您确实需要检查异常和标志,但它们不应该相互依赖。例如。您的示例捕获了异常,但是依赖于该标志退出循环。如您所见,这是行不通的。
在捕获到异常时,您需要break
退出while循环,或者在循环中将try/catch
置于 outside (确保捕获异常使您退出工作)循环)。
因此,以下任一服务应满足:
while (!Thread.interrupted()) { // out of the loop when flag is raised
try {
//......
}
catch (InterruptedException e) {
break;//exit loop when exception thrown
}
}
// Or:
try {
while (!Thread.interrupted()) { // exit loop when flag is raised
//.....
}
}
catch (InterruptedException e) {
// no need to break, as we're already outside
}
答案 2 :(得分:1)
这是带有打印语句的代码。
public static void interrupted() throws InterruptedException {
Runnable task = () ->
{
while (true && !Thread.currentThread().isInterrupted()) {
System.out.println("task executing");
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// this prints false.
System.out.println(Thread.currentThread().isInterrupted());
}
}
};
一旦处理了中断状态,线程就不再被视为处于中断状态,因此循环继续进行。