父线程只能发送一次中断信号?在迭代中有了sleep(),while(!Thread.currentThread().isInterrupted())会失效?

来源:5-5 中断信号

Panda_io

2019-08-17

感谢老师的前面的解惑让我弄清楚了interrupt不是实时被检测而是当遇到阻塞时会触发这个检测(我个人理解为被动检测),“interrupt是一个信号,这个信号并不是实时检测的,也不是每行代码都能检测的,在本例中,只有程序运行到sleep时,才能检测到线程被中断了,然后会抛出异常;而代码在计算其他内容时,比如执行if (num % 100 == 0)时,是无法感知到中断已经发生了的。所以问题不在于休眠时间的长短,而是在于“每次循环都有休眠,所以终究会运行到休眠的sleep代码,在sleep时才会抛出异常”。

但是我后面想主动去检测这个中断,第一种设想我是在父线程不断给子线程发送中断信号,就算第一次处于休眠期间并且清除了中断信号那么没关系,我后面的中断请求总有一个你可以在while(!Thread.currentThread().isInterrupted())接收到吧,然而当我运行代码的时候发现,当第一次抛出异常后程序就卡住了,难道父线程只能像启动线程那样也只能使用一次中断?我查看了下interrupt()源码,首先会检查路径,然后检查blocker是否为空,不为空设置中断标志,最后执行这个 blocker.interrupt(this)再结束方法(这行代码没太懂),如果为空仅设置中断标识然后结束方法。看了几遍发现还是没有头绪,代码如下:图片描述

运行结果,运行中抛出第一次中断错误后会卡在那儿,如箭头所示,并未结束。

图片描述

第二种设想,既然上面不行,那我手动一次一次运行看有没有可能不抛出中断异常直接结束,然而我发现历史总是惊人的相似。

图片描述
图片描述

那么问题来了,当迭代中出现slee()时为什么在while(!Thread.currentThread().isInterrupted())检测不到中断,只能在sleep()时候检查到中断信号呀(而且已经把休眠时间设置得足够短了,测试次数也足够多了),还有父线程只能发送一次信号吗?麻烦老师给小菜鸡解释一下,老师时间宝贵,我也试着自己去寻找答案,实现想不通才求助于您,救救孩子吧。

写回答

2回答

悟空

2019-08-17

同学你自己探索、寻找答案的精神值得鼓励,我看了你的问题,也写了代码,解答如下:

结论:

第一,父线程并不是只能发送一次信号,你第一个代码里的

while (true) {
    thread.interrupt();
}

那么确实就是会一直给子线程发送中断信号,不止一次。


第二,迭代中出现slee()时为什么在while(!Thread.currentThread().isInterrupted())检测不到中断,只能在sleep()时候检查到中断信号?

实际上并不是while(!Thread.currentThread().isInterrupted())检测不到中断,而是你的for循环里什么都没写,代码自动优化把你的for循环忽略了,所以相当于你的Runnable里面的while循环就只做了一件事:Thread.sleep(1),这样一来,自然在被中断时候一定是在sleep的,因为它没有做别的事。


第三,图片里为什么程序不停止?

//img.mukewang.com/szimg/5d57d1e8094ee76911340644.jpg

这并不是子线程不停止,其实子线程已经被中断并且停止了,但是由于主线程的代码

while (true) {
    thread.interrupt();
}

是无限循环,所以整个程序不会停止。


第四,我写了代码验证了你的问题,验证结果是while(!Thread.currentThread().isInterrupted())完全是可以检测到中断发生的,并不是一定要在sleep里才能检测到的,代码如下:

public static void main(String[] args) throws InterruptedException {
    Runnable runnable = () -> {
        while (!Thread.currentThread().isInterrupted()) {
            for (int i = 0; i < 100; i++) {
                System.out.print("1");
            }
            System.out.println();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("未在while处中断");
            }
            for (int i = 0; i < 100; i++) {
                System.out.print("2");
            }
            System.out.println();

        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
    Thread.sleep(1000);
    while (true) {
        thread.interrupt();
    }
}

根据每个电脑的CPU不同,你把Thread.sleep(1000)里面的时间可以做调整,然后多次运行,会发现有的时候是停止在了sleep期间,所以会打印"未在while处中断":

//img.mukewang.com/szimg/5d57d2ba090461eb18400764.jpg

而有的时候,会停止在while(!Thread.currentThread().isInterrupted())期间,所以不会打印“未在while处中断”:

//img.mukewang.com/szimg/5d57d2dd090f503d18300740.jpg


希望解答了你的困惑~

9
9
波粒二象性max
666
2020-05-15
共9条回复

黑子的一生

2019-09-11

老师真的好啊!真的好负责!么么哒

1
1
Panda_io
那确实是个超级好的老师!
2019-09-14
共1条回复

线程八大核心+Java并发原理及企业级并发解决方案

完整的并发知识网络+丰富的工作内容分享+50余道并发高频面试题

2512 学习 · 939 问题

查看课程