关于 线程中断与阻塞 的其他疑问:
- 抛出InterruptedException后,interrupt标志位会复位?所以在catch InterruptedException 异常时要注意恢复现场(就是重新把这个被阻塞的线程在interrupt一遍)?对!
- 线程被interrupt之后还能恢复就绪状态吗?还没搞明白
- wait,sleep,join时,调interrupt会抛异常?对!
文章目录
1. 证明Thread.interrupt并不能真正使线程阻塞
package com.example.juc.interrupt;
/**
* 练习线程中断与阻塞
* Thread类自带interrupt方法
*/
public class ThreadInterrupt {
// 1.测试给某个线程设置中断后,是否仍然能执行任务
// 2.练习使用Thread的sleep,join,wait方法
// 3.查看某线程设置了interrupt之后,再执行sleep,join,wait方法效果会如何
private static void task() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
Thread.currentThread().interrupt();
for (int i = 11; i < 20; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
new Thread(() -> {
task();
}).start();
}
}
2. interrupt方法不能单独使用
interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用
package com.example.juc.interrupt;
/**
* 练习线程中断与阻塞
* Thread类自带interrupt方法
*/
public class ThreadInterrupt {
// interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用
private static void task(String taskName) {
while(!Thread.currentThread().isInterrupted()) {
System.out.println(taskName + " : " + 11);
}
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
task("a");
});
t1.start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
t1.interrupt(); // 模拟中断:这里t1的打印会有两种结果:t1完全打印不出来或者打印一段就停了
}).start();
}
}
3. 设置被中断线程的执行优先级,方便观察中断(其实是变种的demo2)
package com.example.juc.interrupt;
/**
* 练习线程中断与阻塞
* Thread类自带interrupt方法
*/
public class ThreadInterrupt {
// interrupt真正用法,让被中断线程搭配Thread.currentThread().isInterrupted()使用
private static void task(String taskName) {
while(!Thread.currentThread().isInterrupted()) {
System.out.println(taskName + " : " + 11);
}
System.out.println("中断了。。。。。。");
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
task("a");
});
t1.setPriority(10); // 调高了t1的优先级,让t1先跑,方便观察t2通知t1中断的处理效果
t1.start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
t1.interrupt(); // 中断t1
// 新问题:线程处理完中断是否会复位。。
}).start();
}
}
4. 线程中断的其他实现方式
4.1 用volitle实现线程中断
package com.example.juc.interrupt;
/**
* 通过volitle关键字实现中断
*/
public class InterruptByVolitle {
private static volatile boolean isStop = false;
// 模拟一个线程通知另一个线程中断
public static void main(String[] args) {
new Thread(() -> {
while(true) {
if(isStop) {
System.out.println(Thread.currentThread().getId() + "线程中断了" + isStop);
break;
} else {
System.out.println(Thread.currentThread().getId() + "----");
}
}
}).start();
// 对主线程休眠的新理解:休眠会让出CPU的执行权,这样做是为了让其他线程有更大的概率获取到时间片
// 从而倾向呈现预测的效果
try {
System.out.println(Thread.currentThread().getId() + "线程准备休眠");
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
isStop = true;
}).start();
}
}
4.2 用AtomicBoolean实现线程中断
package com.example.juc.interrupt;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 通过AtomicBoolean关键字实现中断
*/
public class InterruptByVolitle {
// private static volatile boolean isStop = false;
private static AtomicBoolean isStop = new AtomicBoolean(false);
// 模拟一个线程通知另一个线程中断
public static void main(String[] args) {
new Thread(() -> {
while(true) {
if(isStop.get()) {
System.out.println(Thread.currentThread().getId() + "线程中断了" + isStop.get());
break;
} else {
System.out.println(Thread.currentThread().getId() + "----");
}
}
}).start();
// 对主线程休眠的新理解:休眠会让出CPU的执行权,这样做是为了让其他线程有更大的概率获取到时间片
// 从而倾向呈现预测的效果
try {
System.out.println(Thread.currentThread().getId() + "线程准备休眠");
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
isStop.set(true);
}).start();
}
}
5. 高频面试题:使用interrupt的注意事项
5.1 问题demo
先演示一个有问题的写法:通知一个已经处于sleep/wait/join状态的线程中断
有问题的点在于:catch 了 InterruptedException时没有恢复中断状态,这会导致程序一直死循环
package com.example.juc.interrupt;
/**
* Thread#interrupt的使用注意事项
*/
public class Interrupt2 {
private static synchronized void task() {
while(true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getId() + "线程中断了");
break;
}
// 线程在没被中断的情况下,每间隔4s打印一次
try {
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
System.out.println("异常!"); // 此处异常处理要小心!!!
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + "----");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
task();
});
t1.start();
try {
System.out.println(Thread.currentThread().getId() + "线程准备休眠");
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
t1.interrupt(); //通知t1中断
}).start();
}
}
5.2 问题demo的执行结果
执行结果:
5.3 解决方案
异常处理时的正确做法:中断恢复
5.4 Thread#interrupt文档详解
查看Thread#interrupt的方法描述:
如果线程被中断时已经调用了wait,sleep,join方法,那么会在接收到InterruptedException后擦除中断标志位
6. 演示线程wait/notify时使用interrupt报错
package com.example.juc.interrupt;
/**
* Thread#interrupt的使用注意事项
*/
public class Interrupt2 {
private synchronized void task(Interrupt2 obj) {
while(true) {
if(Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getId() + "线程中断了");
break;
}
// 线程在没被中断的情况下,每间隔4s打印一次
try {
obj.wait(4000); // wait可行,拿到了对象锁,不然会报IllegalMonitorStateException异常
//this.wait(4000); // wait可行,拿到的是对象锁
//Thread.currentThread().wait(4000); // wait用错了
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 正确的做法:在catch InterruptedException 后进行中断恢复
e.printStackTrace();
}
System.out.println(Thread.currentThread().getId() + "----");
}
}
public static void main(String[] args) {
Interrupt2 obj = new Interrupt2();
Thread t1 = new Thread(() -> {
obj.task(obj);
});
t1.start();
try {
System.out.println(Thread.currentThread().getId() + "线程准备休眠");
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
t1.interrupt(); //通知t1中断
}).start();
}
}
6.1 总结:使用wait/notify 必须与 synchronized 一起使用
源码如下