Java 线程中断和LockSupport
1.线程中断机制概念
首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
其次,在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的协商机制―---中断,也即中断标识协商机制。
中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程请求这条线程中断,此时究竟该做什么需要你自己写代码实现。
每个线程对象中都有一个中断标识位,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
2.中断三大Api解释
2.1 Thread.interrupt()
interrupt()
是一个用于通知线程它应该中断的方法。当线程在执行过程中调用 interrupt()
方法时,它会设置一个标志位(即中断标志),通知线程中断。
- 作用:通知线程中断,并通过设置中断标志使线程能够响应中断请求。
- 行为 :
interrupt()
并不会直接停止线程,它只是设置了线程的中断标志。线程可以通过检查中断标志来决定是否终止任务。
代码示例:
java
public class InterruptExample extends Thread {
@Override
public void run() {
try {
// 模拟长时间任务
for (int i = 0; i < 10; i++) {
System.out.println("Task running " + i);
Thread.sleep(1000); // 会抛出 InterruptedException
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted!");
}
}
public static void main(String[] args) throws InterruptedException {
InterruptExample thread = new InterruptExample();
thread.start();
Thread.sleep(3000); // 主线程休眠 3 秒
thread.interrupt(); // 中断子线程
}
}
interrupt()
设置线程的中断标志,并不直接停止线程,线程可以通过判断中断标志来决定是否退出。
2.2 Thread.isInterrupted()
返回中断状态
2.3 Thread.interrupted()
该方法检查当前线程是否已被中断,并且在检查之后会清除中断标志。它是静态方法,所以可以调用它来检测当前线程的中断状态,并清除中断标志。
3.如何停止一个正在运行中的线程
虽然可以直接暴力STOP 但是一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止.
对于线程自己而言,应该不断的去检查状态值,如果识别到应该停止,他应该退出
所以我们可以这样
1.通过volatile
的布尔变量实现
2.通过AtomicBoolean
实现
3.通过线程的中断机制实现
4.哪些阻塞可以被中断,哪些不能
4.1可中断的阻塞
针对线程处于由sleep
, wait
, join
,LockSupport.park
等方法调用产生的阻塞状态时,调用interrupt方法,会抛出异常InterruptedException
,同时会清除中断标记位,自动改为false。
4.2不可中断的阻塞
- java.io包中的同步Socket I/O
- java.io包中的同步I/O
- Selector的异步I/O
sychronized
加的锁
具体来说,当对一个线程,调用interrupt()时:
①如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。
②如果线程处于被阻塞状态(例如处于sleep, wait, join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。在catch块中应该加上一行代码:Thread.currentThread().interrupt();
为什么要写这个?
这是维持状态。sleep(),wait()方法抛出InterruptException异常后会清除中断标志,即把中断标志设为false。而你又捕获了InterruptException,这时你基本上阻止任何更高级别的方法/线程组注意到中断。这可能会导致问题。通过调用Thread.currentThread().interrupt(),你可以设置线程的中断标志(即把中断标志设为true),因此更高级别的中断处理程序会注意到它并且可以正确处理它。
当前线程的中断标识为true,是不是线程就立刻停止?
实例方法interrupt()仅仅是设置线程的中断状态位为true,不会停止线程。中断只是一种协商机制,修改中断标识位仅此而已,不是立刻stop打断
sleep方法抛出InterruptedException后,中断标识也被清空置为false,我们在catch没有通过调用th.interrupt()方法再次将中断标识置为true,这就导致无限循环了
5.LockSupport是什么
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语,其中park()和unpack()而作用分别是阻塞线程和解除阻塞线程
5.1三种让线程等待和唤醒的方法
- 方式一:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
- 方式二:使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程
- 方式三:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程