关于线程的interrupt与sleep问题
目录
关于这个问题我找到了很多帖子,他们都说:
先sleep,后才能interrupt。 sleep是阻塞,线程会暂时停在这里。interrupt 是打断。只有阻塞的线程被打断了,才会报这个异常。
然后,我作为初学者经过实际的代码测试的时候就很蒙。我们看组代码:
情况一:自定义线程中写interrupt与sleep
public class SubThread4 extends Thread {
@Override
public void run() {
System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("线程的id:" + Thread.currentThread().getId());
System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("当前状态:" + this.isInterrupted());
try {
System.out.println(this.isInterrupted());
System.out.println("打断后:"+this.isInterrupted());
Thread.currentThread().interrupt();
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
//打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
//事实证明先打断后sleep才会抛出异常。。。
//在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
// System.out.println("调用过sleep后:"+this.isInterrupted());
System.out.println("进入异常");
System.out.println(this.isInterrupted());
return;
}
System.out.println("当前状态:" + this.isInterrupted());
System.out.println("这个线程执行完了!");
}
}
public class SubThread4 extends Thread {
@Override
public void run() {
System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("线程的id:" + Thread.currentThread().getId());
System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("当前状态:" + this.isInterrupted());
try {
System.out.println(this.isInterrupted());
System.out.println("打断后:"+this.isInterrupted());
sleep(3000);
Thread.currentThread().interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
//打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
//事实证明先打断后sleep才会抛出异常。。。
//在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
// System.out.println("调用过sleep后:"+this.isInterrupted());
System.out.println("进入异常");
System.out.println(this.isInterrupted());
return;
}
System.out.println("当前状态:" + this.isInterrupted());
System.out.println("这个线程执行完了!");
}
}
这两组代码只有sleep(3000);和Thread.currentThread().interrupt();这两行代码调换位置,这一点不同,其余部分完全一样。
但结果好像和网上说的“先sleep,后才能interrupt,抛异常”顺序不一样啊!实际上它们是一样的,网上的分析都光分析了内部原理,内部原理确实是先sleep后interrupt就会抛出异常,但他们都没说实际编码是个啥样,我也是想了半天才晕过来,在实际编程中我们的sleep调用的是Thread为我们封装好的sleep方法,也就是说sleep已经封装好了,你不可能在sleep的每一毫秒过程中都去检测或者调用interrupt方法。编码中的sleep是一个整体,它需要完全睡完,之后才能执行第二行的Thread.currentThread().interrupt();,而这个时候它都已经睡完了当然不会发生sleep中打断的情况,所以编码的这个时候 先sleep(3000);后Thread.currentThread().interrupt();是没有任何异常的。
相反的,先使用Thread.currentThread().interrupt();将线程的中断标志调为true,这个时候我们再来写sleep(3000);它会发现中断标记已经为true了,它没法再阻塞这个线程了,就会出现异常并捕获异常。注意:这个时候它会捕获异常是不假,但它并不会返回,我们需要手动在catch中加一个return;返回并结束线程,这个也是线程的特性,它只会捕获但并不能将异常抛出后结束线程。
所以以后要用这种方法结束线程,编码时是先写interrupt后写sleep这样才能抛异常。
情况二:主线程中写
这种就是大概这样你写好了自定义线程代码(就把Thread.currentThread().interrupt();删掉了interrupt();放在主线程里面):
public class SubThread4 extends Thread {
@Override
public void run() {
System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("线程的id:" + Thread.currentThread().getId());
System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("当前状态:" + this.isInterrupted());
try {
System.out.println(this.isInterrupted());
System.out.println("打断后:"+this.isInterrupted());
// Thread.currentThread().interrupt();
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
//打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
//事实证明先打断后sleep才会抛出异常。。。
//在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
// System.out.println("调用过sleep后:"+this.isInterrupted());
System.out.println("进入异常");
System.out.println(this.isInterrupted());
return;
}
System.out.println("当前状态:" + this.isInterrupted());
System.out.println("这个线程执行完了!");
}
}
主方法:
public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+" , id = "+Thread.currentThread().getId());
SubThread4 t4 = new SubThread4();
System.out.println("当前时间:"+System.currentTimeMillis());
t4.start();
t4.interrupt();
System.out.println("睡眠之后的时间:"+System.currentTimeMillis());
}
}
结果:
你可能觉得interrupt在线程start之后但还是抛出异常信息了,是不是编码按照先sleep后interrupt的顺序执行了呢? 错! 代码执行顺序依旧是先interrupt后sleep所以才抛得异常信息。 因为主线程里面开别的线程的方式虽然是直接调用start方法,但start方法调用后线程并没有直接执行,而是将该线程交给了线程调度器(schedule) 线程真正什么时候运行是不确定的。(但是一般都是main线程执行完才开始执行别的线程的 ,这里的一般是指JDK1.8在IDEA2018上的执行顺序,没错这就是我电脑的环境。。。XD)
所以综上所述,这种方式,还是执行了先interrupt方法后执行了sleep方法才抛出的异常
再换一种情况
代码:
public class SubThread4 extends Thread {
@Override
public void run() {
System.out.println("当前线程的名字是:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("线程的id:" + Thread.currentThread().getId());
System.out.println("睡眠之后:" + Thread.currentThread().getName() + System.currentTimeMillis());
System.out.println("当前状态:" + this.isInterrupted());
// try {
// System.out.println(this.isInterrupted());
// System.out.println("打断后:"+this.isInterrupted());
Thread.currentThread().interrupt();
// sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// //打断后你不处理异常,继续执行循环,线程重新继续跑,所以interrupt变成false了
// //事实证明先打断后sleep才会抛出异常。。。
// //在子线程的run方法中,如果有异常要处理只能捕获处理,不能抛出处理,因为原run方法不能抛出
// // System.out.println("调用过sleep后:"+this.isInterrupted());
System.out.println("进入异常");
System.out.println(this.isInterrupted());
return;
// }
System.out.println("当前状态:" + this.isInterrupted());
System.out.println("这个线程执行完了!");
}
}
主线程:
public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+" , id = "+Thread.currentThread().getId());
SubThread4 t4 = new SubThread4();
System.out.println("当前时间:"+System.currentTimeMillis());
t4.start();
// try {
// sleep(4000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Thread.currentThread().interrupt();
try {
sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
// t4.interrupt();
System.out.println("睡眠之后的时间:"+System.currentTimeMillis());
}
}
结果:
我们可以很明显的看到主线程被打断了
主线程中这一句代码没有执行。
但子线程执行了,你可能会在一开始觉得子线程永远在主线程运行完才执行,主线程要停了子线程就不执行了。