JUC高级三:LockSupport与线程中断
创始人
2025-05-30 18:07:59

JUC高级三:LockSupport与线程中断

1. 线程中断机制

1.1 什么是中断?

  1. 首先
    • 一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。
      所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
  2. 其次
    • 在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断
    • 中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。

1.2 如何中断一个线程?

  1. 若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true;
  2. 接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。

每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;
通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。

1.3 中断相关API方法

image-20230319134213118

image-20230319134654399

1.4 使用中断标识停止线程

1.4.1 方式一: 通过一个volatile变量实现

如果一个变量被volatile那么这个变量就具备可见性,在高并发情况下通过修改变量状态来决定程序和系统的运行

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class InterruptDemo {static volatile boolean isStop = false;public static void main(String[] args) {new Thread(() -> {while(true){if(isStop){System.out.println("-----isStop = true,程序结束。");break;}System.out.println("------hello isStop");}},"t1").start();//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {isStop = true;},"t2").start();}
}

执行效果:程序输出一秒------hello isStop后检测到isStop为ture程序被中断

image-20230319135700669

1.4.2 方式二:通过AtomicBoolean实现

AtomicBoolean就是原子性的Boolean,因为带有原子性天生就不需要加锁

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;public class InterruptDemo {static AtomicBoolean atomicBoolean = new AtomicBoolean(false);public static void main(String[] args) {new Thread(() -> {while(true){if(atomicBoolean.get()){System.out.println("-----atomicBoolean.get() = true,程序结束。");break;}System.out.println("------hello atomicBoolean");}},"t1").start();//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {atomicBoolean.set(true);},"t2").start();}
}

执行结果:程序输出一秒------hello atomicBoolean后检测到atomicBoolean为ture程序被中断

image-20230319140053546

1.4.3 方式三:通过Thread类自带的中断api方法实现(本章主要内容)

image-20230319134654399

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class InterruptDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("-----isInterrupted() = true,程序结束。");break;}System.out.println("------hello Interrupt");}}, "t1");t1.start();try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {t1.interrupt();//修改t1线程的中断标志位为true},"t2").start();}
}

执行结果:t2线程调用t1线程的interrupt()方法后t1线程被中断

image-20230319140618914

1.5 interrupt()方法源码分析

image-20230319141615578

1.6 isInterrupted()方法与interrupted源码分析

image-20230319142111914

总结:

当对一个线程,调用 interrupt() 时:

  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
  • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。

1.7 当前线程的中断标识为true,是不是就立刻停止?

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class InterruptDemo {public static void main(String[] args) {//中断为true后,并不是立刻stop程序Thread t1 = new Thread(() -> {for (int i = 1; i <= 300; i++) {System.out.println("------i: " + i);}System.out.println("t1.interrupt()调用之后02: "+Thread.currentThread().isInterrupted());}, "t1");t1.start();System.out.println("t1.interrupt()调用之前,t1线程的中断标识默认值: "+t1.isInterrupted());try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }//实例方法interrupt()仅仅是设置线程的中断状态位设置为true,不会停止线程t1.interrupt();//活动状态,t1线程还在执行中System.out.println("t1.interrupt()调用之后01: "+t1.isInterrupted());try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }//非活动状态,t1线程不在执行中,已经结束执行了。System.out.println("t1.interrupt()调用之后03: "+t1.isInterrupted());}
}

执行结果:在执行interrupt方法后t1仍然在执行只是将中断标志位设为了true

image-20230319143911554

1.8 InterruptedException怎么处理?

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class InterruptDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("-----isInterrupted() = true,程序结束。");break;}try {Thread.sleep(500);} catch (InterruptedException e) {
//                    Thread.currentThread().interrupt();//???????  //线程的中断标志位为false,无法停下,需要再次掉interrupt()设置truee.printStackTrace();}System.out.println("------hello Interrupt");}}, "t1");t1.start();try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {t1.interrupt();//修改t1线程的中断标志位为true},"t2").start();}
}

执行结果:程序出现InterruptedException后会一直执行不会停止,因为我们调用interrupt方法时t1线程正处于sleep状态,根据之前api的说明会爆出InterruptedException并且会将我们中断状态清除

image-20230319144225344

解决方法:在catch代码块中让中断标志为true,防止出现InterruptedException异常程序无法停止

image-20230319144706005

1.9 静态方法interrupted的理解

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class InterruptDemo {public static void main(String[] args) {System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());System.out.println("111111");Thread.currentThread().interrupt();///----false---> trueSystem.out.println("222222");System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());}
}

执行结果:

image-20230319145219083

2. LockSupport

image-20230319145935145

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。

2.1 所有方法

选中的为常用方法,该类无构造方法

image-20230319150206363

2.2 3种让线程等待和唤醒的方法

2.2.1 方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;public class LockSupportDemo {static Object objectLock = new Object();public static void main(String[] args) {new Thread(() -> {//暂停几秒钟线程
//            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }synchronized (objectLock){System.out.println(Thread.currentThread().getName()+"\t"+"---come in");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒");}},"t1").start();//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()+"\t"+"---发出通知");}},"t2").start();}
}

执行结果:正常情况等待然后被唤醒

image-20230319150634285

2.2.1.1 异常情况一:wait方法和notify方法,两个都去掉同步代码块

出现IllegalMonitorStateException异常

image-20230319150929243

2.2.1.2 异常情况二: 将notify在wait方法前面执行

线程t1将永远处于等待状态因为唤醒语句notify在wait方法前执行

image-20230319151103435

2.2.2 方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {static Lock lock = new ReentrantLock();static Condition condition = lock.newCondition();public static void main(String[] args) {new Thread(() -> {//暂停几秒钟线程
//            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }lock.lock();try{System.out.println(Thread.currentThread().getName()+"\t"+"---come in");condition.await();System.out.println(Thread.currentThread().getName()+"\t"+"---被唤醒");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}},"t1").start();new Thread(() -> {lock.lock();try{condition.signal();System.out.println(Thread.currentThread().getName()+"\t"+"---发出通知");}finally {lock.unlock();}},"t2").start();}
}

执行结果:正常状态线程等待然后被唤醒

image-20230319151504730

总结:

  1. wait和notify方法必须要在同步块或者方法里面,且成对出现使用
  2. 先wait后notify才OK

2.2.2.1 异常情况一: 去掉lock/unlock

仍然出现IllegalMonitorStateException异常

image-20230319151735294

2.2.2.2 异常情况二:先signal后await

线程t1将永远处于等待状态

image-20230319151904534

总结:

  1. Condtion中的线程等待和唤醒方法之前,需要先获取锁
  2. 一定要先await后signal,不要反了

Object和Condition使用的限制条件:

线程先要获得并持有锁,必须在锁块(synchronized或lock)中

必须要先等待后唤醒,线程才能够被唤醒

2.2.3 方式三:LockSupport类中的park等待和unpark唤醒

image-20230319152326989

通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作

2.2.3.1 主要方法
image-20230319150206363

调用LockSupport.park()时

permit默认是零所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为零并返回。

调用LockSupport.unpark()时

调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park()方法会立即返回。

2.2.3.2 源码阅读

image-20230319153208333

2.2.3.3 代码实例
package site.zhourui.juc.interrupt;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;public class LockSupportDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(Thread.currentThread().getName() + "\t" + "---come in");LockSupport.park();System.out.println(Thread.currentThread().getName() + "\t" + "---被唤醒");}, "t1");t1.start();try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName()+"\t"+"---发出通知");},"t2").start();}
}

执行结果:

解决了方式一和方式二中的问题

无锁块报错问题以及先唤醒后等待程序一直等待问题

image-20230319153955464

2.2.3.4 异常情况:多次park多次unpark

线程一直无法被唤醒,因为park颁发的许可证只能有一个,就算颁发多个也只能有一个,但是我们unpark两次就需要两个许可证所以无法唤醒线程t1

image-20230319154524819

相关内容

热门资讯

舆情监测系统有哪些优势,TOO... 舆情监测系统是一种基于大数据技术的舆情分析工具,可以帮助企业、政府等机构实时监控公众对...
【Linux】基础IO流(上) 文章目录1. 预备知识2. 回忆C接口fopenfputsfprintfsnprintf追加方式——...
设计模式(二十七)----行为... 1 概述 如上图,设计一个软件用来进行加减计算。我们第一想法就是使用工具类ÿ...
精心整理前端主流框架学习路径 版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog....
typescript声明 前言 “d.ts”文件用于为 TypeScript 提供有关用 JavaScript 编写的 API...
HashMap源码分析 Java源码系列:下方连接 http://t.csdn.cn/Nwzed 文章目录...
一、基础算法3:二分 模板题+... 文章目录算法模板整数二分算法模板浮点数二分算法模板模板题数的范围原题链接题目题解数的三次方根原题链接...
Essential C++复习... 好久没写代码了,很多东西都忘记了。复盘一下C++编写基础头文件 与 输...
三十五、DRF中的过滤、Dja... 一、DRF中的过滤 REST framework 的通用列表视图的默认行为是返回模型管理器的整个查询...
基于RZ/G2UL Corte... 以太网接口是一种广泛应用的网络接口,它可以在不同的场合实现不同的功能。例如࿰...
nginx-会话保持-3 基于LNMP在负载均衡集群上部署wordpress(低配版) 在讲解会话...
【BBuf的CUDA笔记】九,... 0x0. 背景 随着年纪越来越大,读代码越来越困难,如果你发现看不懂同事...
springboot: myb... 目录 需求01:  根据不同类型 查询不同的订单名, 1. 书写订单 类型转换方法   2. 使用方...
最前端|什么是低代码?与传统开... 目录一、低代码介绍二、背景趋势三、低代码与传统代码开发(一)低代码能否替...
物流成本总是超标?一招教你降本... 先来看一个数据,中国仓储物流管理指标与世界标杆数据对比 第三方物流比例:...
exec家族与system函数 exec家族函数NAMEexecl, execlp, execle, execv, execvp, ...
Pots 倒水问题 简单搜索&&进阶搜索 - Virtual Judge (vjudge.net) 【题目描述】 两个...
Linux内核通杀提权漏洞(C... 之前Linux官方爆出了"脏牛"漏洞(代号:Dirty COW,漏洞编号...
如何解决“sxs.dll错误”... 在使用计算机的过程中,可能会遇到各种各样的错误。其中一个常见的错误是“sxs.dll错...
【数据结构】夯实基础|线性表刷... 作者:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享...
自定义类型【C进阶】 文章目录😺结构体🟥结构体内存对齐内存对齐的规则内存对齐的意义修改默认...
latex 知识点总结 文章目录(一)latex 知识点总结(二)l...
【小猫爪】AUTOSAR学习笔... 【小猫爪】AUTOSAR学习笔记15-BswM模块前言1 BswM模块简介2 BswM功能简介2.1...
PMSM矢量控制笔记(1.2)... 前言:永磁同步电机的转子包括永磁体、转子铁芯、轴承等机械结构等,其转子可...
关于面试时HA(RAC)会问到... 1.什么是RAC(Real Application Cluster)&#...
关于车身广告的规定(车身广告是... 今天给各位分享关于车身广告的规定的知识,其中也会对车身广告是否合法进行解释,如果能碰巧解决你现在面临...
尾灯熏黑是怎么弄的?(尾灯熏黑... 本篇文章极速百科给大家谈谈尾灯熏黑是怎么弄的?,以及尾灯熏黑后怎么弄掉对应的知识点,希望对各位有所帮...
2030年汽油车就不能开了吗?... 本篇文章极速百科给大家谈谈2030年汽油车就不能开了吗?汽油车什么时候淘汰,以及2025汽油车会淘汰...
法拉利各个款式的红色跑车你钟爱... 今天给各位分享法拉利各个款式的红色跑车你钟爱哪一款的知识,其中也会对法拉利红色车多少钱一辆进行解释,...
一尺等于多少厘米(一尺等于多少... 本篇文章极速百科给大家谈谈一尺等于多少厘米,以及一尺等于多少厘米长度对应的知识点,希望对各位有所帮助...