本篇我们来聊一下线程中断。
线程中断大体分3种情况:
1>中断当前处于运行状态的线程
2>中断当前处于阻塞状态的线程
3>无法中断持有同步锁的线程
实现机制
java 线程实现中断机制原理其实不难理解:
java 给每一个线程都设置一个boolean类型中断标识,用来标明当前线程是否请求中断。当线程对象调用interrupt() 方法时,会将当前线程标识置为false。表示请求中断此线程。 注意此时仅仅是标记要中断,但不会执行任何动作。在编码时,我们可以在run方法中加入人为控制逻辑, 通过调用Thread.currentThread().isInterrupted()或者Thread.interrupted()来检测线程的中断标识是否被置位,进而实现线程中断控制。
第一种:中断当前处于运行状态的线程
例:线程正常执行某个逻辑,条件改变,需要取消任务执行
public class MyThread1 extends Thread {
public void run() {
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程正在正常执行.....");
}
System.out.println("线程结束....");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread1 thread = new MyThread1();
thread.start();
//模拟三秒后条件不满足,停止线程
Thread.sleep(3000);
//设置中断标志
thread.interrupt();
}
在main方法启动MyThread1线程, 线程正常执行,3秒之后,对MyThread1线程发起中断请求。在MyThread1线程run方法中,循环检查中断标识,Thread.currentThread().isInterrupted()一但监测到线程中断请求,立马终止循环,结束线程。
第二种:中断当前处于阻塞状态的线程
例:线程休眠等待条件满足,当条件提前满足,马上中断休眠,开始执行
public class MyThread2 extends Thread {
public void run() {
while(true){
if(!Thread.currentThread().isInterrupted()){
try {
System.out.println("线程休眠中等待资源.....");
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("结束休眠");
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
System.out.println("正常运行.....");
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread2 thread = new MyThread2();
thread.start();
//模拟三秒后条件提前满足,终止休眠继续执行线程
Thread.sleep(3000);
//设置中断标志
thread.interrupt();
}
在main方法中启动MyThread2线程,线程开始执行,遇到sleep方法然后阻塞10s,此时的main方法阻塞3s时间到,醒来调用interrupt()给MyThread2线程设置了请求中断标识(false),与此同时,MyThread2立马醒来,先将中断标识复位(true), 然后抛出一个中断异常InterruptedException 。这里要注意,在catch中需要对线程再发起一次中断请求,否则逻辑又进入修改状态。
注意:
除了hread.sleep()方法之外,还有Thread.join()、object.wait()或者调用阻塞的i/o等,注意,IO抛出的不是InterruptedException 而是其他异常,而且在被中断的情况下也不会退出阻塞状态.
第三种:无法中断持有同步锁的线程
例:线程死锁时请求中断
public class Resouces{
//线程1线程2争夺lock1 跟 lock2,
//程序中线程1持有lock1,等lock2, 线程2持有lock2,等lock1相互等待,遍死锁了
public void doSomething(Object lock1, Object lock2){
//持有1锁
synchronized (lock1){
System.out.println(Thread.currentThread().getName() + ",进来了....");
try {
//暂停一下效果更明显
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//持有2锁
synchronized (lock2){
System.out.println(Thread.currentThread().getName() + ",也进来了....");
System.out.println("正常执行....");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
//共享资源
final Resouces resouces = new Resouces();
//互相锁
final Object lock1 = new Object();
final Object lock2 = new Object();
//线程1
Thread t1 = new Thread(new Runnable() {
public void run() {
//持有2把锁时才可执行
resouces.doSomething(lock1, lock2);
}
},"t1");
//线程2
Thread t2 = new Thread(new Runnable() {
public void run() {
//持有2把锁时才可执行
resouces.doSomething(lock2, lock1);
}
},"t2");
//线程启动
t1.start();
t2.start();
Thread.sleep(3000);
//暂停3s后,给线程发起中断请求
t1.interrupt();
t2.interrupt();
}
注意
线程之所以无法中断,原因在于在线程死锁后,线程都进入了阻塞状态,按照常规的中断机制无法完成中断操作,所以说无法中断。但无绝对如果采用带有超时机制获取方式:reentrantLock.tryLock(longtimeout, TimeUnit unit),等待时间内,如果线程发起中断请求,还是会抛一个InterruptedException , 这样依然可以控制线程中断。