一. 基础理解
先了解几个问题:
怎么中断一个运行中的线程 多线程中断标识是什么 中断后线程是不是就立刻停止运行了
注意: 一个线程不应该由其他线程来强制中断或停止,而是应该有线程自己自行停止,所以Thread.stop, Thread.suspend, Thread.resume都已经被废弃 每个线程中都有一个用于表示线程是否被中断的标识,当标识为true时说明被中断,false是表示未被中断,通过调用interrupt()方法设置,可以在别的线程中调用,也可以在别的线程中调用, 注意点这是java提供的一种中断线程的中断机制,interrupt()该方法也仅仅将线程对象设置为中断状态,实际线程并不能立即停止,在后续操作中需要不断的检测线程是否被中断 几个中断相关的api
如何退出一个线程
面试题: 如何优雅的退出或停止一个线程: 需要中断的线程不停监听中断标识状态,一旦判断为中断状态,则跳出逻辑,有一下几种方式
volatile 方式 AtomicBoolean Thread中自带的中断api
volatile 与 AtomicBoolean 中断线程示例
volatile 示例
声明一个中断线程标识interruptFlags true为中断,使用 volatile 修饰 在 voiatileInterruptTest() 方法中开启了两个线程a与b, a线程执行时判断中断标识,如果为true说明要中断了,跳出业务 b线程中修改中断标识,例如根据某种业务逻辑,判断a线程需要停止运行了,就设置标识为true,a线程判断状态为true,就跳出
import java. util. concurrent. TimeUnit ;
public class InterruptDemo {
private volatile boolean interruptFlags = false ;
public void voiatileInterruptTest ( ) throws InterruptedException {
new Thread ( ( ) -> {
while ( true ) {
if ( interruptFlags) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程中断" ) ;
break ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程正常执行" ) ;
}
} , "a" ) . start ( ) ;
TimeUnit . SECONDS. sleep ( 5L ) ;
new Thread ( ( ) -> {
interruptFlags = true ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 将线程a设置为中断状态" ) ;
} , "b" ) . start ( ) ;
}
public static void main ( String [ ] args) throws InterruptedException {
InterruptDemo demo = new InterruptDemo ( ) ;
demo. voiatileInterrupt ( ) ;
}
}
AtomicBoolean 示例
将中断线程标识设置为AtomicBoolean 原子类型变量 a线程执行时先判断原子类型变量 b线程中根据业务逻辑修改a线程的中断标识
private AtomicBoolean isStop = new AtomicBoolean ( false ) ;
public void atomiInterrupt ( ) throws InterruptedException {
new Thread ( ( ) -> {
while ( true ) {
if ( isStop. get ( ) ) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程中断" ) ;
break ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程正常执行" ) ;
}
} , "a" ) . start ( ) ;
TimeUnit . SECONDS. sleep ( 5L ) ;
new Thread ( ( ) -> {
isStop. set ( true ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 将线程a设置为中断状态" ) ;
} , "b" ) . start ( ) ;
}
public static void main ( String [ ] args) throws InterruptedException {
InterruptDemo demo = new InterruptDemo ( ) ;
demo. atomiInterrupt ( ) ;
}
总结: 不管是volatile 方式还是 Atomic原子类方式,1. 其底层关注点都是变量线程可见性的问题,一个线程中修改变量,另外一个线程中能及时拿到修改后的值, 2. 要中断的线程中要判断这个代表中断的标识,拿到状态,需要停止则跳出
Thread中自带的中断api示例
示例代码
主要通过 interrupt()设置线程中断标志位位中断状态true, isInterrupted()获取线程的中断标志位,如果是true并清除中断标识ture修改为false t1线程中执行业务逻辑时先isInterrupted()获取当前线程的中断标志位,如果为中断状态则跳出 t2线程中根据业务逻辑,拿t1线程设置t1它自己为中断状态,
public void threadApiInterrupt ( ) throws InterruptedException {
Thread t1 = new Thread ( ( ) -> {
while ( true ) {
if ( Thread . currentThread ( ) . isInterrupted ( ) ) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程中断" ) ;
break ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程正常执行" ) ;
}
} , "t1" ) ;
t1. start ( ) ;
TimeUnit . SECONDS. sleep ( 5L ) ;
Thread t2 = new Thread ( ( ) -> {
t1. interrupt ( ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 将线程a设置为中断状态" ) ;
} , "t2" ) ;
t2. start ( ) ;
}
public static void main ( String [ ] args) throws InterruptedException {
InterruptDemo demo = new InterruptDemo ( ) ;
demo. threadApiInterrupt ( ) ;
}
有个主意点: 在设置线程中断后,被设置中断的线程不会停止,只是将线程中断标志设置为true,没有其它任何操作,想让被设置的线程中断停止,需要程序员手动去实现,例如获取中断标志,手动break并且: 假设线程处于被阻塞状态例如(sleep, wait, join等),此时如果对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并且抛出InterruptedException异常
阻塞状态线程中断时异常解决
上面说到了线程处于被阻塞状态例如(sleep, wait, join等),此时如果对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并且抛出InterruptedException异常, 在业务中也就是说当前这个线程是阻塞状态,然后一调用中断,由阻塞退出了,interrupt中断状态true变为false,并抛出了异常,这样就会造成: 1 原阻塞线程不再阻塞, 2. 中断标识由true变为了false 解决以上问题(在第3步中)
public void threadApiInterruptSleep ( ) throws InterruptedException {
Thread t1 = new Thread ( ( ) -> {
while ( true ) {
if ( Thread . currentThread ( ) . isInterrupted ( ) ) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程中断" ) ;
break ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 线程正常执行" ) ;
try {
TimeUnit . SECONDS. sleep ( 5L ) ;
} catch ( InterruptedException e) {
Thread . currentThread ( ) . interrupt ( ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程再次中断 " ) ;
e. printStackTrace ( ) ;
}
}
} , "t1" ) ;
t1. start ( ) ;
TimeUnit . SECONDS. sleep ( 2L ) ;
Thread t2 = new Thread ( ( ) -> {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 将线程a设置为中断状态" ) ;
t1. interrupt ( ) ;
t1. isInterrupted ( ) ;
} , "t2" ) ;
t2. start ( ) ;
}
public static void main ( String [ ] args) throws InterruptedException {
InterruptDemo demo = new InterruptDemo ( ) ;
demo. threadApiInterruptSleep ( ) ;
}
二. Thread中自带的中断底层分析
查看 interrupt() 设置线程为中断状态接口源码,发现调用到底层是native的interrupt0()方法
public void interrupt ( ) {
if ( this != Thread . currentThread ( ) )
checkAccess ( ) ;
synchronized ( blockerLock) {
Interruptible b = blocker;
if ( b != null ) {
interrupt0 ( ) ;
b. interrupt ( this ) ;
return ;
}
}
interrupt0 ( ) ;
}
private native void interrupt0 ( ) ;
查看 isInterrupted() 源码,其底层也是调用native的isInterrupted(boolean ClearInterrupted),并且会传递一个false,将中断标识设置为false
public boolean isInterrupted ( ) {
return isInterrupted ( false ) ;
}
private native boolean isInterrupted ( boolean ClearInterrupted ) ;
三. 总结
以前设置中断线程的方法: volatile 或 AtomicBoolean 方式,都是基于变量可见性的特性: 例如 a线程执行时判断中断标识,如果为true说明要中断了,跳出业务, b线程中修改中断标识,例如根据某种业务逻辑,判断a线程需要停止运行了,就设置标识为true,a线程判断状态为true,就跳出 讲一下规范中有个要求: 一个线程不应该由其他线程来强制中断或停止,而是应该有线程自己自行停止,所以Thread.stop, Thread.suspend, Thread.resume都已经被废弃 Thread中自带的中断api: 通过 interrupt()设置线程中断标志位位中断状态true, isInterrupted()获取线程的中断标志位,如果是true并清除中断标识ture修改为false, 例如
t1线程中执行业务逻辑时先isInterrupted()获取当前线程的中断标志位,如果为中断状态则跳出 t2线程中根据业务逻辑,拿t1线程设置t1它自己为中断状态,
使用interrupt()方法中断线程的注意点:
线程并不会自动中断,需要调用isInterrupted()获取状态,手动去break 如果线程处于被阻塞状态例如(sleep, wait, join等),对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并抛出InterruptedException异常
如何解决中断阻塞状态线程抛出异常问题: tryCatch捕获InterruptedException异常,当发生该异常时在catch中再次调用interrupt()设置中断标识