中断方法无法关闭JVM

时间:2019-06-28 00:27:00

标签: java multithreading

我在玩以下示例:

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中被阻塞,则将设置该线程的中断状态,并且它将立即从选择操作中返回,可能具有非零值,就像选择器的唤醒方法一样被调用。

     

如果以上条件均不成立,则将设置该线程的中断状态。

     

中断未运行的线程不会产生任何效果。

3 个答案:

答案 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)

InterruptedExceptionThread.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());
            }
         }
      };

一旦处理了中断状态,线程就不再被视为处于中断状态,因此循环继续进行。