为了账号安全,请及时绑定邮箱和手机立即绑定

不关闭JVM的中断方法

不关闭JVM的中断方法

幕布斯6054654 2023-02-23 09:52:05
我正在玩以下示例: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上面的 2 是可能的,因为主线程休眠了 10 秒3 一旦主线程唤醒,它就会调用衍生线程的中断4 在 Runnable 任务中,我们正在检查 isInterrupted,因此它应该被触发并且生成的线程应该退出,从而允许 JVM 退出。然而,这并没有发生,eclipse/jvm 显示生成的线程仍在运行 while 循环这是控制台:task executingtask executingjava.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at basics.StopEx.lambda$1(StopEx.java:39)at java.lang.Thread.run(Thread.java:748)task executingtask executingtask executingtask executing........关于中断的 java 文档似乎表明调用“睡眠”会导致不同的行为(下面突出显示)-有人可以阐明这里到底发生了什么以及为什么吗?中断此线程。除非当前线程正在中断自身(这始终是允许的),否则将调用此线程的 checkAccess 方法,这可能会导致抛出 SecurityException。如果此线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法或 join()、join(long)、join(long, int) 方法时被阻塞, sleep(long), or sleep(long, int), 这个类的方法,然后它的中断状态将被清除,它会收到一个 InterruptedException。如果此线程在 InterruptibleChannel 上的 I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,线程将收到 java.nio.channels.ClosedByInterruptException。如果此线程在 java.nio.channels.Selector 中被阻塞,则线程的中断状态将被设置,并且它将立即从选择操作返回,可能返回一个非零值,就像选择器的唤醒方法被调用一样。如果前面的条件都不成立,那么将设置该线程的中断状态。中断一个不活动的线程不需要有任何影响。
查看完整描述

3 回答

?
萧十郎

TA贡献1815条经验 获得超12个赞


这是因为中断标志如何为线程工作。


当您中断一个线程时,基本上只会发生一件事:设置中断标志,仅此而已。然后取决于该线程正在运行的代码是否确实执行了操作。


一般的想法是,如果可以的话,你会中止你正在做的事情并抛出一些合理的异常。


java 内置的一些方法保证这正是它们的工作方式。您可以轻松识别它们:它们都被声明为 throw 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% 的情况下,它会在阻塞睡眠时发生。然后标志将为假,并抛出异常。在不太可能发生的情况下,它会在其外部处理时发生,而 while 条件将拾取它。


请注意,如果您提高了中断标志并调用了类似的方法Thread.sleep,该方法将通过清除标志并抛出来立即InterruptedException完成执行。不管中断发生在哪里,上面的例子都会做同样的事情(静默退出run方法,中断标志将被清除)。


查看完整回答
反对 回复 2023-02-23
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

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 之外(确保捕获异常使您脱离循环)。


因此,应提供以下任一服务:


    while (!Thread.currentThread().isInterrupted()) { // out of the loop when flag is raised 

        try {   

            //......

        } 

        catch (InterruptedException e) { 

            break;//exit loop when exception thrown

        }

    }


    // Or:

    try {   

        while (!Thread.currentThread().isInterrupted()) { // exit loop when flag is raised

            //.....

        }

    } 

    catch (InterruptedException e) { 

        // no need to break, as we're already outside

    }


查看完整回答
反对 回复 2023-02-23
?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

这是带有打印语句的代码。


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());

            }

         }

      };

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


查看完整回答
反对 回复 2023-02-23
  • 3 回答
  • 0 关注
  • 78 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信