章
目
录
本文主要讲解Java线程interrupt方法相关内容,我们一起来学习一下吧!
虽然有很多同学学过了java线程相关的知识,但是对interrupt始终不能深入理解,我们将会通过案例来实操讲解下interrupt的作用。
1、打断线程阻塞状态
interrupt可以打断线程的阻塞状态,在线程调用sleep、wait和join方法进入阻塞状态时,我们可以通过调用线程的interrupt方法来将其打断,线程将会被唤醒并抛出InterruptedException中断异常,我们看下下面的代码:
@Slf4j
public class ThreadInterrupt {
@Test
public void testInterrupt(){
// 创建t1线程
Thread t1 = new Thread(()->{
log.debug("t1 start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.debug("t1 interrupt");
e.printStackTrace();
}
log.debug("t1 over");
},"t1");
// 启动
t1.start();
// 主线程休眠1s保证t1进入sleep状态
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断
t1.interrupt();
// 确保中断结束
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 中断标记:{}", t1.isInterrupted());
}
}
我们发现中断最后中断标记为false,是因为interrupt在打断sleep、wait和join方法后会将中断标记重新置为false,如果你在上述代码中,如果删除打印中断标记前的sleep,有机会在抛出中断异常之前打印出中断标记为true的情况,因为interrupt方法有如下两个特点:
- Thread.interrupt()方法用于中断一个线程。当调用此方法时,将设置线程的中断标志位为true。
- 如果目标线程正在被阻塞(如在sleep、wait或join方法中)或者正在等待获取一个内置锁(如synchronized块或方法),那么它将抛出一个InterruptedException异常,并清除线程的中断状态,也就是会将中断标志为置为false。
2.打断正常线程
interrupt方法除了可以打断阻塞的线程,还可以打断结束正常的线程,而且方式还比较优雅,我们先看以下代码:
public static void main(String[] args) {
// 创建t1线程
Thread t1 = new Thread(()->{
while (true) {
System.out.println("running");
}
},"t1");
// 启动
t1.start();
// 主线程休眠1s保证t1运行起来
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断
t1.interrupt();
}
你会发现t1线程会陷入死循环打印,即使interrupt被调用,也不会停止运行,因此想要停止线程,我们需要判断中断标志位,来退出循环结束线程:
public static void main(String[] args) {
// 创建t1线程
Thread t1 = new Thread(()->{
while (true) {
// 判断打断标志位,若为true退出循环
boolean isInterrupted = Thread.currentThread().isInterrupted();
if(isInterrupted) {
break;
}
System.out.println("running");
}
},"t1");
// 启动
t1.start();
// 主线程休眠1s保证t1运行起来
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断
t1.interrupt();
}
这样我们可以优雅地结束线程的运行了。
3.(终止)模式之两阶段终止
两阶段终止:英文称为Two Phase Termination
即:在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。
3.1. 错误思路
● 使用线程对象的 stop() 方法停止线程
○ stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
● 使用 System.exit(int) 方法停止线程
○ 目的仅是停止一个线程,但这种做法会让整个程序都停止
3.2. 两阶段终止模式
@Slf4j
public class TwoPhaseTermination {
/**
* 监控线程
*/
private Thread monitor;
/**
* 启动监控线程
*/
public void start(){
monitor = new Thread(()->{
while (true) {
Thread current = Thread.currentThread();
if(current.isInterrupted()) {
log.debug("料理后事...");
break;// 结束线程
}
// 执行监控-如果在正常运行中被中断,中断标记会置为true退出循环
log.debug("执行监控");
// 睡眠1s-如果在sleep时被interrupt会在抛出异常后清除中断标记,即恢复为false,导致无法结束线程,因此需要在catch中再次中断
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
// 将中断标记置为true
current.interrupt();
}
}
});
monitor.start();
}
/**
* 停止监控线程
*/
public void stop(){
monitor.interrupt();
}
}
再写个测试方法:
@Test
public void testTwoPhaseTermination() throws InterruptedException {
TwoPhaseTermination t1 = new TwoPhaseTermination();
t1.start();
Thread.sleep(4500);
t1.stop();
}
由运行记录可见,该监控线程这次正好在sleep时被中断了,然后料理线程即将结束的后事,最后线程停止运行。
4.interrupted()方法
interrupted()方法也是用来判断线程是否被打断,与isInterrupted()方法不同的是isInterrupted()判断完后不会清除中断标记,而interrupted()判断完后会清除中断标记。
5.interrupt打断park线程
打断 park 线程, 不会清空打断状态,我们写个代码案例:
public static void main(String[] args) throws InterruptedException {
testInterruptPark();
}
public static void testInterruptPark() throws InterruptedException {
Thread t1 = new Thread(()->{
log.debug("park");
LockSupport.park();
log.debug("unpark");
log.debug("中断标志:{}", Thread.currentThread().isInterrupted());
},"t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
注意:
1)如果不执行t1.interrupt方法,只会打印park,LockSupport.park()让程序阻塞,无法继续运行
2)t1.interrupt执行中断后,中断标记没有被清除,仍然为true,如果后续再次执行LockSupport.park()则不会再阻塞程序,而是直接继续往下运行,也就是park方法失效了,如下:
log.debug("park");
LockSupport.park();
log.debug("unpark");
log.debug("中断标志:{}", Thread.currentThread().isInterrupted());
LockSupport.park();
log.debug("park此时无效,继续执行");
如果想要park继续生效,就要恢复中断标记,而恢复就需要执行interrupted()方法,如下:
log.debug("park");
LockSupport.park();
log.debug("unpark");
log.debug("中断标志:{}", Thread.currentThread().interrupted());
LockSupport.park();
log.debug("park此时有效,程序阻塞");
6. 总结
以上就是Java线程interrupt方法详解的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!