思考问题:
多线程的中断标识是什么?
如何停止中断运行中的线程?
中断后线程是不是就立刻停止运行了?
一.什么是中断?
首先,一个线程不应该由其他线程来强制中断或者停止,而是应该由线程自己自行停止。
(即:线程之间不可以互相杀对方)
其次,在java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。
因此,java提供了一种用于停止线程的机制--中断
又可以理解为:中断 --> 中断协商机制 (理解 interrupt :并不是立刻,马上,now,stop一个线程)
中断只是一种协作机制,java没有给中断增加任何语法,中断的过程完全需要程序员自己去实现。
若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设置为true;
接着,你需要自己编写代码,不断的检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断,
此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个标识,用于表示线程是否被中断,该标识位为true表示中断,为false表示未中断。
通过调用线程对象的interrupt方法将该线程的标识位设置为true;可以在别的线程中调用,也可以在自己的线程中调用
中断的相关API方法:
public void interrupt() 实例方法,实例方法interrupt(),仅仅是设置线程的中断状态为true,不会停止线程
public static boolean interrupted()
静态方法,Thread.interrupted();
判断线程是否被中断,并且清除中断状态
这个方法做了2件事,1.返回当前线程的中断状态,2.将当前线程的中断状态设置为false
public boolean isInterrupted() 实例方法,判断当前线程是否被中断(通过检查中断标志位)
二.如何停止中断运行中的线程?
1.通过一个volatile变量实现
2.通过AtomicBoolean
3.通过Thread类自带的中断API实例的方法实现
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 1. 通过volatile关键字实现线程中断
* 2. 通过AtomicBoolean原子类实现线程中断
* 3. 使用线程中断相关的API实现线程中断
*/
public class InterruptDemo {
static volatile boolean isStop = false;
static AtomicBoolean flag = new AtomicBoolean(false);
public static void main(String[] args) {
// method_1_volatile();
// method_2_atomic();
method_3_interrupt();
}
private static void method_1_volatile() {
new Thread(()->{
while (true){
System.out.println(Thread.currentThread().getName() + "线程正在执行中...");
if (isStop){
System.out.println("----------中断了-------");
break;
}
}
}, "t1").start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
isStop = true;
},"t2").start();
}
private static void method_2_atomic() {
new Thread(() -> {
while (true) {
System.out.println("线程正在执行中...");
if (flag.get()) {
System.out.println("----------中断了-------");
break;
}
}
}, "t1").start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
flag.set(true);
}, "t2").start();
}
private static void method_3_interrupt() {
Thread t1 = new Thread(() -> {
while (true) {
System.out.println("线程正在执行中...");
if (Thread.currentThread().isInterrupted()) { //判断当前线程是否中断
System.out.println("----------中断了-------");
break;
}
}
}, "t1");
t1.start();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() ->{
t1.interrupt(); // 中断t1线程
},"t2").start();
}
}
三.总结:
具体来说,当对一个线程调用interrupt()时,
1.如果线程处于正常活动的状态,那么会将该线程的中断标识设置为true,仅此而已
被设置中断标志的线程继续正常运行,不受影响
所以,interrupt()并不能真正的中断线程,需要被调用的线程自己配合才行
2.如果线程处于被阻塞状态(sleep,wait,join),别的线程调用当前线程对象的interrupt()方法,
那么线程将立即退出被阻塞状态,并且抛出一个InterruptException异常
private static void m6() {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted = " + Thread.currentThread().isInterrupted() +" 程序结束");
break;
}
try {
// 加上这个睡眠报错:
/* 如果线程处于被阻塞状态(例如处于sleep,wait,join等状态),在别的线程中调用当前线程对象的interrupt方法,
那么线程将立即退出被阻塞状态(中断状态被清除),
并且抛出一个InterruptException异常 : java.lang.InterruptedException: sleep interrupted
*/
Thread.sleep(500);
} catch (InterruptedException e) { // 解决方案: 在异常中重新把它打断
// 因为看源码的注释知道,抛出InterruptedException这个异常后,线程的中断标志位会被重新设置为false
// 再次总结:sleep方法抛出InterruptedException异常后,中断标识也被清空设置为flase,
// 如果我们在catch没有通过调用thread.interrupt()方法,再次将中断标识设置为true,就会导致无限循环
Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println("--- Interrupted ");
}
}, "t1");
t1.start();
// 执行1秒
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
// 第二个线程将isStop设置为true
new Thread(() -> {
t1.interrupt(); // 修改t1线程的中断标志位为true 注意:仅仅是设置线程的中断状态为true,不会停止线程
}, "t2").start();
}