赞
踩
目录
2、(代码)locks目录下:LockSyncDemo.java
3、(代码)locks目录下:ReEntryLockDemo.java
4、(代码)locks目录下:DeadLockDemo.java
四十、多线程锁之objectMonitor和synchronized锁小总结
5、(代码)interrupt目录下:InterruptDemo.java
四十四、中断机制之通过AtomicBoolean实现线程中断停止
四十六、中断机制之interrupt和isInterrupted源码分析
6、(代码)interrupt目录下:InterruptDemo2.java
7、(代码)interrupt目录下:InterruptDemo3.java
8、(代码)interrupt目录下:InterruptDemo4.java
五十一、LockSupport之wait和notify实现等待和唤醒
9、(代码)LockSupport目录下:LockSupportDemo.java
五十二、LockSupport之await和signal实现等待和唤醒
五十三、LockSupport之park和unpark入门简介
五十四、LockSupport之park和unpark编码实战
说说Java“锁”事
从轻松的乐观锁和悲观锁开讲
通过8种情况演示锁运行案例,看看我们到底锁的是什么
公平锁和非公平锁
可重入锁(又名递归锁)
死锁及排查
写锁(独占锁)/读锁(共享锁)
自旋锁SpinLock
无锁->独占锁->读写锁->邮戳锁
无锁->偏向锁->轻量锁->重量锁
大厂面试题复盘
并发编程高级面试解析
一、Synchronized相关问题
1、Synchronized用过吗,其原理是什么?
2、你刚才提到获取对象的锁,这个“锁”到底是什么?如何确定对象的锁?
3、什么是可重入性,为什么说Synchronized是可重入锁?
4、JVM对Java的原生锁做了哪些优化?
5、为什么说Synchronized是非公平锁?
6、什么是锁消除和锁粗化?
7、为什么说Synchronized是一个悲观锁?乐观锁的实现原理又是什么?什么是CAS,它有
8、乐观锁一定就是好的吗?
二、可重入锁ReentrantLock及其他显示锁相关问题
1、跟Synchronized相比,可重入锁ReentrantLock其实现原理有什么不同?
2、那么请谈谈AQS框架是怎么回事儿?
3、请尽可能详尽地对比下Synchronized和ReentrantLock的异同。
4、ReentrantLock是如何实现可重入性的?
1、你怎么理解java多线程的?怎么处理并发?线程池有那几个核心参数?
2、Java加锁有哪几种锁?我先说了synchronized,刚讲到偏向锁,他就不让我讲了
3、简单说说lock
4、hashmap的实现原理?hash冲突怎么解决?为什么使用红黑树?
5、spring里面都使用了哪些设计模式?循环依赖怎么解决?
6、项目中哪个地方用了countdownlatch,怎么使用的?
悲观锁(狼性锁):认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改
synchronized关键字和Lock的实现类都是悲观锁
适合写操作多的场景,先加锁可以保证写操作时数据正确。
显示的锁定之后再操作同步资源
乐观锁(佛系锁):认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。
在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。
如果这个数据没有被更新,当前线程将自己修改的数据成功写入。
如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等
判断规则
1 版本号机制Version
2 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
适合读操作多的常见,不加锁的特点能够使其读操作的性能大幅度提升。
乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是一句话:
乐观锁一般有两种实现方式
采用Version版本号机制
CAS(Compare-and-Swap,即比较并替换)算法实现
伪代码说明
【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
- package com.nanjing.juc.locks;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- * 多线程锁之8锁案例编码演示
- *
- * @author xizheng
- * @date 2023-08-14 15:22:42
- */
- class Phone{
- public static synchronized void sendEmail(){
- try {
- TimeUnit.SECONDS.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("-----sendEmail");
- }
-
- public static synchronized void sendSMS(){
- System.out.println("-----sendSMS");
- }
-
- public void hello(){
- System.out.println("------hello");
- }
- }
-
- /**
- * 题目:谈谈你对多线程锁的理解,8锁案例说明
- * 口诀:线程 操作 资源类
- * 8锁案例说明:
- * 1 标准访问有ab两个线程,请问先打印邮件还是短信
- * 2 sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信
- * 3 添加一个普通的hello方法,请问先打印邮件还是hello
- * 4 有两部手机,请问先打印邮件还是短信
- * 5 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
- * 6 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
- * 7 有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信
- * 8 有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信
- *
- * 笔记总结:
- * 1-2
- * * * 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
- * * * 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
- * * * 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
- * 3-4
- * * * 加个普通方法后发现和同步锁无关
- * * * 换成两个对象后,不是同一把锁了,情况立刻变化。
- *
- * 5-6 都换成静态同步方法后,情况又变化
- * 三种 synchronized 锁的内容有一些差别:
- * 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁->实例对象本身,
- * 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
- * 对于同步方法块,锁的是 synchronized括号内的对象
- *
- * 7-8
- * 当一个线程视图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
- * * *
- * * * 所有的普通同步方法用的都是同一把锁-实例对象本身,就是new出来的具体实例对象本身,本类this
- * * * 也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
- * * *
- * * * 所有的静态同步方法用的也是同一把锁-类对象本身,就是我们说过的唯一模板Class
- * * * 具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
- * * * 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁才能获取锁。
- */
- public class Lock8Demo {
- public static void main(String[] args) {
- Phone phone = new Phone();
- Phone phone2 = new Phone();
-
- new Thread(() -> {
- phone.sendEmail();
- },"a").start();
-
- //暂停毫秒,保证a线程先启动
- try {
- TimeUnit.MILLISECONDS.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(() -> {
- // phone.sendSMS();
- // phone.hello();
- phone2.sendSMS();
- },"b").start();
- }
- }

synchronized有三种应用方式
8种锁的案例实际体现在3个地方
作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;
作用于代码块,对括号里配置的对象加锁。
作用域静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;
从字节码角度分析synchronized实现
javap -c ***.class文件反编译
synchronized同步代码块
javap -c .\LockSyncDemo.class(文件反编译)
synchronized同步代码块:实现使用的是monitorenter和monitorexit指令
synchronized普通同步方法
synchronized普通同步方法:调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会将先持有monitor锁,然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor
synchronized静态同步方法
ACC_STATIC,ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法
- package com.nanjing.juc.locks;
-
- /**
- * 锁同步演示
- *
- * @author xizheng
- * @date 2023-08-14 16:45:14
- */
- public class LockSyncDemo {
-
- Object object = new Object();
-
- /**
- * monitorenter代表锁监视器获得了并进入
- *
- * 正常执行完毕以后会有
- * monitorexit说明要退出
- * synchronized的底层是依赖monitorenter和monitorexit来保证锁的获取和释放
- */
- public void m1(){
- synchronized (object){
- //也就是说这行代码,只要哪一个线程持有这把锁了,谁持有谁进来,进来以后干活,用完以后再将其释放
- System.out.println("----hello synchronized code block");
- //throw new RuntimeException("------exp");
- }
- }
-
- public synchronized void m2(){
- System.out.println("----hello synchronized m2");
- }
-
- public static synchronized void m3(){
- System.out.println("----hello static synchronized m3");
- }
-
- public static void main(String[] args) {
-
- }
- }

反编译synchronized锁的是什么
面试题:为什么任何一个对象都可以成为一个锁
什么是管程monitor
大厂面试题讲解
【Java集合类】
1、从集合开始吧,介绍一下常用的集合类,哪些是有序的,哪些是无序的
2、hashmap是如何寻址的,哈希碰撞后是如何存储数据的,1.8后什么时候变成红黑树、说下红黑树,红黑树有什么好处
3、concurrenthashmap怎么实现线程安全,一个里面会有几个段segment,jdk1.8后有优化concurrenthashmap吗?分段锁有什么坏处
【多线程JUC】
4、reentrantlock实现原理,简单说下aqs
5、synchronized实现原理,monitor对象什么时候生成的?知道mnitor的monitorenter和monitorexit这两个是怎么保证同步的吗?或者说,这两个操作计算机底层是如何执行的
6、刚刚你提到了synchronized的优化过程,详细说明一下吧。偏向锁和轻量级锁有什么区别?
7、线程池几个参数说下,你们项目中如何根据实际场景设置参数的,为什么cpu密集设置的线程数集型少
什么是管程monitor
在HotSpot虚拟机中,monitor采用ObjectMonitor实现
上述C++源码解读
ObjectMonitor.java->ObjectMonitor.cpp->objectMonitor.hpp
objectMonitor.hpp
每个对象天生都带着一个对象监视器
每一个被锁住的对象都会和Monitor关联起来
提前剧透,混个眼熟
synchronized必须作用域某个对象中,所以Java在对象的头文件存储了锁的相关信息。锁升级功能主要依赖于MarkWord中的锁标志位和释放偏向锁标志位,后续讲解解锁升级时候我们再加深,目前为了承前启后的学习,对下图先混个眼熟即可
Hotspot的实现
从ReentrantLock卖票demo演示公平锁和非公平现象
公平锁 | 是指多个线程按照申请锁的顺序来获取锁,这里类似排队买票,先来的人先买后来的人在队尾排着,这是公平的 Lock lock = new ReentrantLock(true);//true 表示公平锁,先来先得 |
非公平锁 | 是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转或者饥饿的状态(某个线程一致得不到锁) Lock lock = new ReentrantLock(false);//false 表示非公平锁,后来的也可能先获得锁 Lock lock = new ReentrantLock();//默认非公平锁 |
为什么会有公平锁和非公平锁的设计?为什么默认非公平?
1 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的,所以非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间。
2 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销。
可重入锁(又名递归锁)
是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象的是同一个对象),不会因为之前已经获取过还没释放而阻塞。
如果是1个有synchronized修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚。所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。
自己可以获取自己的内部锁
可重入锁种类
隐式锁(即synchronized关键字使用的锁)默认是可重入锁
指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
简单的来说就是:在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
同步块
同步方法
Synchronized的重入的实现机理
显示锁(即Lock)也有ReentrantLock这样的可重入锁。
- package com.nanjing.juc.locks;
-
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- /**
- * 可重入锁
- *
- * @author xizheng
- * @date 2023-08-16 09:54:48
- */
- public class ReEntryLockDemo {
- public synchronized void m1(){
- //指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
- System.out.println(Thread.currentThread().getName()+"\t ----come in");
- m2();
- System.out.println(Thread.currentThread().getName()+"\t ----end in");
- }
- public synchronized void m2(){
- System.out.println(Thread.currentThread().getName()+"\t ----come in");
- m3();
- }
- public synchronized void m3(){
- System.out.println(Thread.currentThread().getName()+"\t ----come in");
- }
-
- static Lock lock = new ReentrantLock();
-
- public static void main(String[] args) {
- // ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
- //
- // new Thread(() -> {
- // reEntryLockDemo.m1();
- // },"t1").start();
-
- new Thread(() -> {
- lock.lock();
- try {
- System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
- lock.lock();
- try {
- System.out.println(Thread.currentThread().getName()+"\t ----come in内层调用");
- } finally {
- lock.unlock();
- }
- } finally {
- //由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
- lock.unlock();//正常情况,加锁几次就要解锁几次
- }
- },"t1").start();
-
- new Thread(() -> {
- lock.lock();
- try {
- System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
- } finally {
- lock.unlock();
- }
- },"t2").start();
- }
- public static void reEntryM1(String[] args) {
- final Object object = new Object();
-
- new Thread(() -> {
- synchronized (object){
- System.out.println(Thread.currentThread().getName() + "\t -----外层调用");
- synchronized (object){
- System.out.println(Thread.currentThread().getName() + "\t -----中层调用");
- synchronized (object){
- System.out.println(Thread.currentThread().getName() + "\t -----内层调用");
- }
- }
- }
- },"t1").start();
- }
- }

Synchronized的重入的实现机理 |
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。 当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。 |
为什么任何一个对象都可以成为一个锁
objectMonitor.hpp
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。
- package com.nanjing.juc.locks;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- * 死锁
- *
- * @author xizheng
- * @date 2023-08-16 12:22:37
- */
- public class DeadLockDemo {
- public static void main(String[] args) {
- final Object objectA = new Object();
- final Object objectB = new Object();
-
- new Thread(() -> {
- synchronized (objectA){
- System.out.println(Thread.currentThread().getName()+"\t 自己持有A锁,希望获得B锁");
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (objectB){
- System.out.println(Thread.currentThread().getName()+"\t 成功获得B锁");
- }
- }
- },"A").start();
-
- new Thread(() -> {
- synchronized (objectB){
- System.out.println(Thread.currentThread().getName()+"\t 自己持有B锁,希望获得A锁");
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (objectA){
- System.out.println(Thread.currentThread().getName()+"\t 成功获得A锁");
- }
- }
- },"B").start();
- }
- }

排查命令(jstack 进程编号)
图形化:win+R 使用命令 jconsole
8锁案例运行,锁的到底是什么?对象锁、类锁
公平锁和非公平锁
可重入锁(又名递归锁)
死锁及排查
为什么任何一个对象都可以成为一个锁
ObjectMonitor中有几个关键属性
_owner | 指向持有ObjectMonitor对象的线程 |
_WaitSet | 存放处于wait状态的线程队列 |
_EntryList | 存放处于等待锁block状态的线程队列 |
_recursions | 锁的重入次数 |
_count | 用来记录该线程获取锁的次数 |
小总结(重要)
指针指向monitor对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个monitor与之关联,当一个monitor被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
LockSupport与线程中断
线程中断机制
从阿里蚂蚁金服面试题讲起
void interrupt() | 中断此线程 |
static boolean interrupted() | 测试当前线程是否已被中断 |
boolean isInterrupted() | 测试此线程是否已被中断 |
如何中断一个运行中的线程??
如何停止一个运行中的线程??
什么是中断机制
大厂面试题中断机制考点
1、如何停止中断运行中的线程?
① 通过一个volatile变量实现
② 通过AtomicBoolean
③ 通过Thread类自带的中断api实例方法实现(在需要中断的线程中不断监听中断状态,一旦发生中断,就执行相应的中断处理业务逻辑stop线程)
2、当前线程的中断标识为true,是不是线程就立刻停止?
3、静态方法Thread.interrupted(),谈谈你的理解
- package com.nanjing.juc.interrupt;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicBoolean;
-
- /**
- * 如何停止中断运行中的线程?
- *
- * @author xizheng
- * @date 2023-08-16 13:32:31
- */
- public class InterruptDemo {
-
- static volatile boolean isStop = false;
- static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
-
- public static void main(String[] args) {
- Thread t1 = new Thread(() -> {
- while (true){
- if(Thread.currentThread().isInterrupted()){
- System.out.println(Thread.currentThread().getName()+"\t isInterrupted()被修改为true,程序停止");
- break;
- }
- System.out.println("t1 -----hello interrupt api");
- }
- },"t1");
- t1.start();
-
- System.out.println("-----t1的默认中断标志位:"+t1.isInterrupted());
-
- //暂停毫秒
- try {
- TimeUnit.MILLISECONDS.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- //t2向t1发出协商,将t1的中断标志位设为true希望t1停下来
- new Thread(() -> {
- t1.interrupt();
- },"t2").start();
- }
-
- private static void m2_atomicBoolean(String[] args) {
- new Thread(() -> {
- while (true){
- if(atomicBoolean.get()){
- System.out.println(Thread.currentThread().getName()+"\t atomicBoolean被修改为true,程序停止");
- break;
- }
- System.out.println("t1 -----hello atomicBoolean");
- }
- },"t1").start();
-
- //暂停毫秒
- try {
- TimeUnit.MILLISECONDS.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(() -> {
- atomicBoolean.set(true);
- },"t2").start();
- }
-
- private static void m1_volatile(String[] args) {
- new Thread(() -> {
- while (true){
- if(isStop){
- System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序停止");
- break;
- }
- System.out.println("t1 -----hello volatile");
- }
- },"t1").start();
-
- //暂停毫秒
- try {
- TimeUnit.MILLISECONDS.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(() -> {
- isStop = true;
- },"t2").start();
- }
- }

代码如上
代码如上
private native void interrupt0();
但凡有JVM的底层知识,native代表调用底层操作系统或者是第三方C语言的函数库
总结
当前线程的中断标识为true,是不是线程就立刻停止?不会
- package com.nanjing.juc.interrupt;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- * 当前线程的中断标识为true,是不是线程就立刻停止?
- *
- * @author xizheng
- * @date 2023-08-16 17:43:15
- */
- public class InterruptDemo2 {
- public static void main(String[] args) {
- //实例方法interrupt()仅仅是设置线程的中断状态位设置为true,不会停止线程
- Thread t1 = new Thread(() -> {
- for (int i = 1; i <= 300; i++) {
- System.out.println("-----: "+i);
- }
- System.out.println("t1线程调用interrupt()后的中断标识02: "+Thread.currentThread().isInterrupted());
- },"t1");
- t1.start();
-
- System.out.println("t1线程默认的中断标识: "+t1.isInterrupted());
-
- //暂停毫秒
- try {
- TimeUnit.MILLISECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- t1.interrupt();
- System.out.println("t1线程调用interrupt()后的中断标识01: "+t1.isInterrupted());
-
- try {
- TimeUnit.MILLISECONDS.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("t1线程调用interrupt()后的中断标识03: "+t1.isInterrupted());//????---false中断不活动的线程不会产生任何影响。
- }
- }

- package com.nanjing.juc.interrupt;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- * 中断demo3
- *
- * @author xizheng
- * @date 2023-08-16 17:54:22
- */
- public class InterruptDemo3 {
- public static void main(String[] args) {
- Thread t1 = new Thread(() -> {
- while (true){
- if(Thread.currentThread().isInterrupted()){
- System.out.println(Thread.currentThread().getName()+"\t "+
- "中断标志位: "+Thread.currentThread().isInterrupted()+" 程序停止");
- break;
- }
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();//为什么要在异常处,再调用一次??
- e.printStackTrace();
- }
-
- System.out.println("-----htllo InterruptDemo3");
- }
- },"t1");
- t1.start();
-
- //暂停几秒钟线程
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(() -> t1.interrupt(),"t2").start();
- }
- }
- /**
- * 1 中断标志位,默认false
- * 2 t2 ----> t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
- * 3 中断标志位true,正常情况,程序停止
- * 4 中断标志位true,异常情况下,InterruptedException,将会把中断状态将被清除,并且将收到InterruptedException。中断标志位false
- * 导致无限循环
- *
- * 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才OK
- */
-
-
-
-
-
-

小总结:中断只是一种协商机制,修改中断标识仅此而已,不是立刻stop打断
private native boolean isInterrupted(boolean var1);
方法的注释也清晰的表达了“中断状态将会根据传入的ClearInterrupted”参数值确定是否重置
所以,
静态方法interrupted将 会清除中断状态(传入的参数ClearInterrupted为true)
实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)
- package com.nanjing.juc.interrupt;
-
- import java.util.concurrent.locks.LockSupport;
-
- /**
- * 中断demo4
- *
- * @author xizheng
- * @date 2023-08-16 18:06:48
- */
- public class InterruptDemo4 {
- public static void main(String[] args) {
- //测试当前线程是否被中断 (检查中断标志),返回一个boolean并清除中断状态,
- // 第二次再调用时中断状态已经被清除,将返回一个false
-
- System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
- System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
- System.out.println("----1");
- Thread.currentThread().interrupt();//中断标志位设置为true
- System.out.println("----2");
- System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
- System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
-
- //LockSupport.park();
-
- Thread.interrupted();//静态方法
-
- Thread.currentThread().isInterrupted();//实例方法
- }
- }

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
下面这句话,后面详细说
LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程
线程等待唤醒机制
1、3种让线程等待和唤醒的方法
①方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
②方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程
③方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
异常1:wait方法和notify方法,两个都去掉同步代码块
异常2:将notify放在wait方法前面,程序无法执行,无法唤醒
小总结:wait和notify方法必须要在同步块或者方法里面,且成对出现使用
先wait后notify才OK
- package com.nanjing.juc.LockSupport;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.LockSupport;
- import java.util.concurrent.locks.ReentrantLock;
- /**
- * @author xizheng
- * @date 2024-01-25 15:24:12
- */
- public class LockSupportDemo {
- static int x = 0;
- static int y = 0;
-
- 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"+System.currentTimeMillis());
- LockSupport.park();
- System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
- }, "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();
-
- }
-
- private static void lockAwaitSignal()
- {
- Lock lock = new ReentrantLock();
- Condition condition = lock.newCondition();
-
- new Thread(() -> {
- try { TimeUnit.SECONDS.sleep(1); } 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();
-
- //暂停几秒钟线程
- //try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
-
- new Thread(() -> {
- lock.lock();
- try
- {
- condition.signal();
- System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
- }finally {
- lock.unlock();
- }
- },"t2").start();
- }
-
- private static void syncWaitNotify()
- {
- Object objectLock = new Object();
-
- new Thread(() -> {
- try { TimeUnit.SECONDS.sleep(1); } 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();
- }
- }

结论:在lock、unlock对里面,才能正确调用condition中线程等待和唤醒的方法
小总结:Condition中的线程等待和唤醒方法,需要先获取锁。一定要先await后signal,不要反了
上述两个对象Object和Condition使用的限制条件
①、线程先要获得并持有锁,必须在锁块(synchronized或lock)中
②、必须要先等待后唤醒,线程才能够被唤醒
LockSupport类中的park等待和unpark唤醒
通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作
2、主要方法
①、阻塞 park() / park(Object blocker)
阻塞当前线程/阻塞传入的具体线程
permit许可证默认没有不能放行,所以一开始调park()方法当前线程就会阻塞,直到别的线程给当前线程的发放permit,park方法才会被唤醒。
②、唤醒 unpark(Thread thread)
唤醒处于阻塞状态的指定线程
调用unpark(thread)方法后,就会将thread线程的许可证permit发放,会自动唤醒park线程,即之前阻塞中的LockSupport.park()方法会立即返回。
1、代码
正常+无锁块要求
之前错误的先唤醒后等待,LockSupport照样支持
重点说明(重要)
面试题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。