为什么有些Thread.sleep()没被中断?

dazhuxiaokai 2011-05-04 12:25:56
看这段代码:
很简单, 20000个线程, 名字是thread1 ~ thread10000thread1-sleep ~ thread10000-sleep
每个threadi-sleep会睡1秒钟,同时threadi会中断它。
    public class InterruptTest {

public static void main(String[] args) throws Exception {
for (int i = 1; i <= 10000; i++) {
Thread t = new Interrupt();
t.setName("thread" + i);
t.start();
}
}

private static class Interrupt extends Thread {
public void run() {
Thread t = new Sleep();
t.setName( getName() + "-sleep" );
t.start();
t.interrupt();
System.out.println( System.currentTimeMillis() + " " +
t.getName() + " " + t.isInterrupted() );
}
}

private static class Sleep extends Thread {
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
return;
}
System.err.println( System.currentTimeMillis() + " No! " +
getName() + " " + isInterrupted() );
}
}

}


我以为最后一句System.err.println()不可能被执行到,因为上面的Thread.sleep()肯定会被中断,然后就到了return。但是stderr里却实有输出:
1304393164531 No! thread949-sleep true
1304393164562 No! thread1128-sleep false


不是每次运行都发生,好像是偶尔才发生。
可以看到,我在每次interrupt()语句后面,都向stdout输出一行。然后我在stdout里面找到了这两行输出,把它们和上面的输出放在一起看:
1304393163531 thread949-sleep true
1304393164531 No! thread949-sleep true
1304393163562 thread1128-sleep true
1304393164562 No! thread1128-sleep false


可以看到,这两个interrupt()语句已经执行完了,而且是在stderr输出的1秒钟前。那么,为什么这两个sleep()没有被中断?

P.S. 我在3台电脑上运行了这个程序很多次,3台都是Windows XP。上述情况只在其中两台发生,这两台都是酷睿双核 2.4GHz。在另一台电脑上没有发生,那台是赛扬 1.5GHz。它们的JDK版本都完全相同,都是1.6。
...全文
653 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
pia0zhong 2011-09-29
  • 打赏
  • 举报
回复
t.interrupt();
System.out.println( System.currentTimeMillis() + " " +t.getName() + " " + t.isInterrupted() );

谁能保证, interrupt之后,JVM执行的是ccer
kafuwei 2011-05-26
  • 打赏
  • 举报
回复
我试了一下,和线程的数量正相关。这应该算jvm的一个bug吧。


for (int i = 1; i <= 10000; i++) {
Thread t = new Interrupt();
t.setName("thread" + i);
t.start();
Thread.sleep(1);
}


我加了一个Thread.sleep(1);问题就不会出现了。
dazhuxiaokai 2011-05-11
  • 打赏
  • 举报
回复
请大家能不帮我试运行一下这个程序,看会不会也有同样的结果。
注:不需要10000个线程,我试过最少500个线程也可以重现出来,平均每运行十几次就会出现一次。
dazhuxiaokai 2011-05-11
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 dreamhunter_lan 的回复:]

同样看不懂这个结果,既然能够到打印No!那一句,就说明一定没有抛出异常,所以不存在它正在睡眠的时候t.interrupt()执行这种情况。我觉得问题出现的println上,你把那些函数拿出来,分别求值后,把它们的值传到println打印。其实最有可能的就是准备打印No!,也就是进入到System.err.println( System.currentTimeMillis() + " No! " ……
[/Quote]
我又试了一遍,把所有println语句都删掉了,然后把sleep时间改成1分钟。
现的我预期的结果是,程序不可能运行1分钟,因为这些sleep都会被中断。但是结果是,有几次果然运行了1分钟。所以结果和上次是一样的,确实有些sleep没有被中断。
彡颵爺 2011-05-05
  • 打赏
  • 举报
回复
20000个线程啊!!!看你们说我都晕咯
dreamhunter_lan 2011-05-05
  • 打赏
  • 举报
回复
同样看不懂这个结果,既然能够到打印No!那一句,就说明一定没有抛出异常,所以不存在它正在睡眠的时候t.interrupt()执行这种情况。我觉得问题出现的println上,你把那些函数拿出来,分别求值后,把它们的值传到println打印。其实最有可能的就是准备打印No!,也就是进入到System.err.println( System.currentTimeMillis() + " No! " + getName() + " " + isInterrupted() );准备计算时间,获得线程名字和线程中断状态(此时还没被设置为true),发生切换了,OS保存现场,切换到其他线程,某个时刻被这个线程又得到机会执行,恢复现场,取时间,取名字,取线程中断状态(OS保存的是被置为true之前的那份,所以还是false),打印出来~~~不知道这算不算是在没有同步情况下对象的状态不一致现象,也就因为这样才需要用volatile关键字??我有点晕了~~哈哈
dazhuxiaokai 2011-05-05
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 chdw 的回复:]

因为在调用start()之后,再调用interrupt之前已经花费了1秒以上,所以sleep(1000)正常的结束了。没有被中断
[/Quote]问题在于,我把时间输出来了,你看:
1304393163531 thread949-sleep true
1304393163562 thread1128-sleep true
1304393164531 No! thread949-sleep true
1304393164562 No! thread1128-sleep false

Interrupt语句的时间是在sleep结束1秒之前!
ChDw 2011-05-05
  • 打赏
  • 举报
回复
因为在调用start()之后,再调用interrupt之前已经花费了1秒以上,所以sleep(1000)正常的结束了。没有被中断
dazhuxiaokai 2011-05-05
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 bao110908 的回复:]

20000 个线程
[/Quote]20000个线程不是问题。而且,只有线程这么多线程的情况下才会发生这个问题。
  • 打赏
  • 举报
回复
20000 个线程
dazhuxiaokai 2011-05-05
  • 打赏
  • 举报
回复
dreamhunter_lan大牛,您能解答一下我4楼的问题吗?
sleepson 2011-05-04
  • 打赏
  • 举报
回复
interrupt执行的时候是需要时间的。。。并不是马上执行。
dazhuxiaokai 2011-05-04
  • 打赏
  • 举报
回复
有谁能解答?
dazhuxiaokai 2011-05-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 dreamhunter_lan 的回复:]
b、此时t启动的线程刚刚睡醒,在打印No!那些东西之前CPU回来,t.interrupt()把t的status设置为true,此时又可能因为CPU切换的关系,可能先打印t.getName() + true,也可能No! t.getName()+ true,反正可以随便想~~~如果t启动的线程睡醒后直接打印出执行打印No!的那句,那么因为t的中断状态还没被设定,所以打印No!+t.getName()+false,然后t.interrup()设定t的中断状态,再打印出t.getName() + true……
[/Quote]
可是,我把时间输出来了,你看:
1304393163562 thread1128-sleep true
1304393164562 No! thread1128-sleep false

前一个输出是在1秒钟以前,所以我觉得这肯定不是您说的情况b,而应该是情况a,所以thread1128-sleep应该被中断,抛出异常。可是为什么它没有?
dreamhunter_lan 2011-05-04
  • 打赏
  • 举报
回复
1、interrupt做的工作只是把一个线程中断状态设置为true,并不中断线程,也就是:如果没啥异常的话,把中断状态设置为true后线程该干啥还是干啥

多线程本来就是很随机的,在Interrupt的run方法中用Sleep的一个实例启动一个线程,然后LZ就可以开始想了:
1、如果此时CPU马上切换,OS将CPU分配给刚刚启动的这个线程,让它休眠大概一秒,然后打印出No! false(为了简单,只列出只要信息),然后切换回来,t.interrupt(),然后打印出t.getName() + true,不过这种情况基本不可能发生,因为那个线程睡眠的时候OS不可能让CPU的闲置的

2、如果此时CPU马上切换,OS将CPU分配给刚刚启动的这个线程,让它休眠大概一秒,此时CPU切换回来(上面那种不太可能的情况是:CPU在那不动,并不切换回来),执行t.interrupt(),有可以分两种情况:a、此时t启动的线程还在睡眠(1s还没过去),试图将一个正在睡眠的线程中断状态设置为true,那么t的中断状态就被清掉,也就是isInterrupted()会返回false,然后抛出一个InterruptedException,LZ的处理方法是return,所以打印出t.getName + false b、此时t启动的线程刚刚睡醒,在打印No!那些东西之前CPU回来,t.interrupt()把t的status设置为true,此时又可能因为CPU切换的关系,可能先打印t.getName() + true,也可能No! t.getName()+ true,反正可以随便想~~~如果t启动的线程睡醒后直接打印出执行打印No!的那句,那么因为t的中断状态还没被设定,所以打印No!+t.getName()+false,然后t.interrup()设定t的中断状态,再打印出t.getName() + true


大概就是这样,因为没有同步,多线程的结果是无法预测的,要考虑所以情况,而这个常常是很难做到的。
Kanepan 2011-05-04
  • 打赏
  • 举报
回复
因为本身起那么多线程就非常耗时间, 而且你才sleep1秒,所以很有可能interrupt没有被执行。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧