当前位置: 首页 > news >正文

固安网站建设网站上线模板

固安网站建设,网站上线模板,湖南建设人力资源网证书查询,品牌建设构建方向3.JUC【Java面试第三季】前言推荐3.JUC06_闲聊AQS面试1.题目说明07_可重入锁理论2.可重入锁说明“可重入锁”这四个字分开来解释可重入锁的种类08_可重入锁的代码验证-上09_可重入锁的代码验证-下3.LockSupport10_LockSupport是什么LockSupport是什么11_waitNotify限制线程等待… 3.JUC【Java面试第三季】前言推荐3.JUC06_闲聊AQS面试1.题目说明07_可重入锁理论2.可重入锁说明“可重入锁”这四个字分开来解释可重入锁的种类08_可重入锁的代码验证-上09_可重入锁的代码验证-下3.LockSupport10_LockSupport是什么LockSupport是什么11_waitNotify限制线程等待唤醒机制wait/notify3种让线程等待和唤醒的方法Object类中的wait和notify方法实现线程等待和唤醒代码小总结12_awaitSignal限制Condition接口中的await后signal方法实现线程的等待和唤醒代码小总结传统的synchronized和lLock实现等待唤醒通知的约束13_LockSupport方法介绍LockSupport类中的park等待和unpark唤醒是什么主要方法14_LockSupport案例解析代码重点说明重要面试题15_AQS理论初步4.AbstractQueuedSynchronizer之AQS先从字节跳动及其它大厂面试题说起前置知识前置知识是什么16_AQS能干嘛AQS为什么是JUC内容中最重要的基石 和AQS有关的进一步理解锁和同步器的关系能干嘛17_AQS源码体系-上AQS初步AQS初识18_AQS源码体系-下AQS内部体系架构AQS自身AbstractQueuedSynchronizer内部类AQS同步队列的基本结构19_AQS源码深度解读01从我们的ReentrantLock开始解读AQSLock接口的实现类基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的ReentrantLock的原理从最简单的lock方法开始看看公平和非公平非公平锁走起方法lock()20_AQS源码深度解读0221_AQS源码深度解读0322_AQS源码深度解读0423_AQS源码深度解读0524_AQS源码深度解读0625_AQS源码深度解读07方法unlock()26_AQS小总结最后前言 2023-2-1 14:57:23 以下内容源自 【尚硅谷Java大厂面试题第3季跳槽必刷题目必扫技术盲点周阳主讲-哔哩哔哩】 仅供学习交流使用 推荐 Java开发常见面试题详解LockSupportAQSSpring循环依赖Redis 3.JUC 06_闲聊AQS面试 1.题目说明 07_可重入锁理论 2.可重入锁 说明 可重入锁又名递归锁 是指在同一个线程在外层方法获取锁的时候再进入该线程的的内层方法会自动获取锁前提是锁对象得是同一个对象 不会因为之前已经获取过还没释放而阻塞。 Java中ReentrantLock和synchronized都是可重入锁可重入锁的一个优点是可一定程度避免死锁。 “可重入锁”这四个字分开来解释 可可以 重再次 入进入 锁同步锁 进入什么 进入同步域即同步代码块/方法或显示锁锁定的代码 一句话 一个线程中的多个流程可以获取同一把锁持有这把同步锁可以再次进入。自己可以获取自己的内部锁。 可重入锁的种类 隐式锁即synchronized关键字使用的锁默认是可重入锁。 同步块同步方法 08_可重入锁的代码验证-上 同步代码块 package juc;/*** 可重入锁;可重复可递归调用的锁在外层使用锁之后在内层仍然可以使用并且不发生死锁这样的锁就叫做可重入锁。** 在一个synchronized修饰的方法或代码块的内部* 调用本类的其他synchronized修饰的方法或代码块时是永远可以得到锁的*/ public class ReEnterLockDemo {static Object objectAnew Object();public static void m1() {new Thread(()-{synchronized (objectA){System.out.println(Thread.currentThread().getName()\t-----外层调用);synchronized (objectA){System.out.println(Thread.currentThread().getName()\t-----中层调用);synchronized (objectA){System.out.println(Thread.currentThread().getName()\t-----内层调用);}}}},t1).start();}public static void main(String[] args) {m1();} } 输出结果 t1 -----外层调用 t1 -----中层调用 t1 -----内层调用 09_可重入锁的代码验证-下 同步方法 package juc;/*** 可重入锁;可重复可递归调用的锁在外层使用锁之后在内层仍然可以使用并且不发生死锁这样的锁就叫做可重入锁。** 在一个synchronized修饰的方法或代码块的内部* 调用本类的其他synchronized修饰的方法或代码块时是永远可以得到锁的*/ public class ReEnterLockDemo {public synchronized void m1() {System.out.println(外);m2();}public synchronized void m2(){System.out.println(中);m3();}public synchronized void m3(){System.out.println(内);}public static void main(String[] args) {new ReEnterLockDemo().m1();} } Synchronized的重入的实现机理。 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。 当执行monitorenter时如果目标锁对象的计数器为零那么说明它没有被其他线程所持有Java虚拟机会将该锁对象的持有线程设置为当前线程并且将其计数器加1。 在目标锁对象的计数器不为零的情况下如果锁对象的持有线程是当前线程那么Java虚拟机可以将其计数器加1否则需要等待直至持有线程释放该锁。 当执行monitorexit时Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。 显式锁即Lock也有ReentrantLock这样的可重入锁。 package juc;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;/*** 在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时是永远可以得到锁的*/ public class ReEnterLockDemo {static Lock locknew ReentrantLock();public static void main(String[] args) {new Thread(()-{lock.lock();lock.lock();try {System.out.println(外层);lock.lock();try {System.out.println(内层);} finally {lock.unlock();}} finally {//这里故意注释实现加锁次数和释放次数不一样//由于加锁次数和释放次数不一样第二个线程始终无法得到锁导致一直在等待lock.unlock();lock.unlock();//正常情况加锁几次就要解锁几次}},t1).start();new Thread(()-{lock.lock();try {System.out.println(Thread.currentThread().getName()\t-----调用开始);} finally {lock.unlock();}},t2).start();} } 3.LockSupport 10_LockSupport是什么 1 为什么要学习LockSupport?1.1 Java----JVM1.2 JUC----AQS----(前置知识可重入锁、 LockSupport) 2学习方法2.1是什么?2.2能干嘛2.3去哪下2.4怎么玩 3 AB----》after [ before synchronized-wait-notify lock-await-signal LockSupport-park-unparkLockSupport是什么 LockSupport Java doc LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。 LockSupport中的park()和 unpark()的作用分别是阻塞线程和解除阻塞线程。 总之比wait/notifyawait/signal更强。 Class LockSupport 是什么 11_waitNotify限制 线程等待唤醒机制wait/notify 3种让线程等待和唤醒的方法 方式1使用Object中的wait()方法让线程等待使用object中的notify()方法唤醒线程 方式2使用JUC包中Condition的await()方法让线程等待使用signal()方法唤醒线程 方式3LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程 Object类中的wait和notify方法实现线程等待和唤醒 代码 正常 package juc;public class LockSupportDemo {static Object objectLock new Object();public static void main(String[] args) {new Thread(()-{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------被唤醒);}},A).start();new Thread(()-{synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()\t------通知);}},B).start();} } 异常1 package juc;/*** 要求:t1线程等待3秒钟3秒钟后t2线程唤龌t1线程继续工作* 以下异常情况:* 2wait方法和notify方法两个都去掉同步代码块后看运行效果* 2.1 异常情况* Exception in thread t1 java.lang.ILLegalMonitorStateException at java.Lang.Object.wait(Native Method)* Exception in thread t2 java.Lang.IllegalMonitorStateException at java.lang.Object.notify(Native Method)* 2.2 结论* Object类中的wait、notify、notifyALL用于线程等待和唤醒的方法都必须在synchronized内部执行必须用到关键字synchronized)。*/ public class LockSupportDemo_2 {static Object objectLock new Object();public static void main(String[] args) {new Thread(()-{ // 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------被唤醒); // }},A).start();new Thread(()-{ // synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()\t------通知); // }},B).start();} } wait方法和notify方法两个都去掉同步代码块 异常情况 A ------come in Exception in thread A Exception in thread B java.lang.IllegalMonitorStateExceptionat java.lang.Object.notify(Native Method)at juc.LockSupportDemo_2.lambda$main$1(LockSupportDemo_2.java:22)at java.lang.Thread.run(Thread.java:745) java.lang.IllegalMonitorStateExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:502)at juc.LockSupportDemo_2.lambda$main$0(LockSupportDemo_2.java:13)at java.lang.Thread.run(Thread.java:745)Process finished with exit code 0 异常2 package juc;import java.util.concurrent.TimeUnit;/*** 要求:t1线程等待3秒钟3秒钟后t2线程唤龌t1线程继续工作** 3 将notify放在wait方法前先执行t1先notify了3秒钟后t2线程再执行ait方法* 3.1程序一直无法结桑1* 3.2结论* 先wait后notify、notifyalL方法等待中的线程才会被唤醒否则无法唤醒*/ public class LockSupportDemo_3 {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------被唤醒);}},A).start();new Thread(()-{synchronized (objectLock){objectLock.notify();System.out.println(Thread.currentThread().getName()\t------通知);}},B).start();} } 将notify放在wait方法前面 程序无法热行无法唤醒 小总结 wait和notify方法必须要在同步块或者方法里面且成对出现使用否则会抛出java.lang.IllegalMonitorStateException。 调用顺序要先wait后notify才OK。 12_awaitSignal限制 Condition接口中的await后signal方法实现线程的等待和唤醒 Condition接口中的await后signal方法实现线程的等待和唤醒与Object类中的wait和notify方法实现线程等待和唤醒类似。 代码 正常 package juc;import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo_2_1 {static Object objectLock new Object();static Lock lock new ReentrantLock();static Condition condition lock.newCondition();public static void main(String[] args) {new Thread(() - {lock.lock();try {System.out.println(Thread.currentThread().getName()\t-----come in);try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()\t-----被唤醒);} finally {lock.unlock();}}, A).start();new Thread(() - {lock.lock();try {condition.signal();System.out.println(Thread.currentThread().getName() \t ------通知);} finally {lock.unlock();}}, B).start();} } 异常1 package juc;import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo_2_2 {static Object objectLock new Object();static Lock lock new ReentrantLock();static Condition condition lock.newCondition();public static void main(String[] args) {new Thread(() - { // lock.lock();try {System.out.println(Thread.currentThread().getName()\t-----come in);try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()\t-----被唤醒);} finally { // lock.unlock();}}, A).start();new Thread(() - { // lock.lock();try {condition.signal();System.out.println(Thread.currentThread().getName() \t ------通知);} finally { // lock.unlock();}}, B).start();} } 异常2 package juc;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_2_3 {static Object objectLock new Object();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);try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()\t-----被唤醒);} finally {lock.unlock();}}, A).start();new Thread(() - {lock.lock();try {condition.signal();System.out.println(Thread.currentThread().getName() \t ------通知);} finally {lock.unlock();}}, B).start();} } 小总结 await和signal方法必须要在同步块或者方法里面且成对出现使用否则会抛出java.lang.IllegalMonitorStateException。 调用顺序要先await后signal才OK。 传统的synchronized和lLock实现等待唤醒通知的约束 线程先要获得并持有锁必须在锁块(synchronized或lock)中必须要先等待后唤醒 线程才能够被唤醒 13_LockSupport方法介绍 LockSupport类中的park等待和unpark唤醒 是什么 通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作官网解释 主要方法 API 阻塞 park() /park(Object blocker) 阻塞当前线程/阻塞传入的具体线程 唤醒 unpark(Thread thread) 唤醒处于阻塞状态的指定线程 14_LockSupport案例解析 代码 正常无锁块要求 package juc;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;public class LockSupportDemo_3_1 {static Object objectLock new Object();static Lock lock new ReentrantLock();static Condition condition lock.newCondition();public static void main(String[] args) {Thread a new Thread(() - {System.out.println(Thread.currentThread().getName() \t -----come in);LockSupport.park();//被阻塞...等待通知等待放行它要通过需要许可证System.out.println(Thread.currentThread().getName() \t -----被唤醒);}, a);a.start();//暂停几秒钟线程try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }Thread b new Thread(() - {LockSupport.unpark(a);System.out.println(Thread.currentThread().getName() \t ------通知);}, b);b.start();} } 之前错误的先唤醒后等待LockSupport照样支持 package juc;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;public class LockSupportDemo_3_2 {static Object objectLock new Object();static Lock lock new ReentrantLock();static Condition condition lock.newCondition();public static void main(String[] args) {Thread a new Thread(() - {//暂停几秒钟线程try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }System.out.println(Thread.currentThread().getName() \t -----come inSystem.currentTimeMillis());LockSupport.park();//被阻塞...等待通知等待放行它要通过需要许可证 //1675241932025--没起作用--1675241932025System.out.println(Thread.currentThread().getName() \t -----被唤醒System.currentTimeMillis());}, a);a.start();Thread b new Thread(() - {LockSupport.unpark(a);System.out.println(Thread.currentThread().getName() \t ------通知);}, b);b.start();} } 解释 sleep方法3秒后醒来执行park无效没有阻塞效果解释如下先执行了unpark(t1)导致上面的park方法形同虚设无效时间一样 重点说明重要 LockSupport是用来创建锁和共他同步类的基本线程阻塞原语。 LockSuport是一个线程阻塞工具类所有的方法都是静态方法可以让线程在任意位置阻塞阻寨之后也有对应的唤醒方法。归根结底LockSupport调用的Unsafe中的native代码。 LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程 LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于10的开关默认是0 调用一次unpark就加1变成1 调用一次park会消费permit也就是将1变成0同时park立即返回。 如再次调用park会变成阻塞(因为permit为零了会阻塞在这里一直到permit变为1)这时调用unpark会把permit置为1。每个线程都有一个相关的permit, permit最多只有一个重复调用unpark也不会积累凭证。 形象的理解 线程阻塞需要消耗凭证(permit)这个凭证最多只有1个。 当调用park方法时 如果有凭证则会直接消耗掉这个凭证然后正常退出。 如果无凭证就必须阻塞等待凭证可用。 而unpark则相反它会增加一个凭证但凭证最多只能有1个累加无放。 面试题 为什么可以先唤醒线程后阻塞线程 因为unpark获得了一个凭证之后再调用park方法就可以名正言顺的凭证消费故不会阻塞。 为什么唤醒两次后阻塞两次但最终结果还会阻塞线程 因为凭证的数量最多为1不能累加连续调用两次 unpark和调用一次 unpark效果一样只会增加一个凭证而调用两次park却需要消费两个凭证证不够不能放行。 package juc;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;public class LockSupportDemo_3_3 {static Object objectLock new Object();static Lock lock new ReentrantLock();static Condition condition lock.newCondition();public static void main(String[] args) {Thread a new Thread(() - {//暂停几秒钟线程try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException e){ e.printStackTrace(); }System.out.println(Thread.currentThread().getName() \t -----come inSystem.currentTimeMillis());LockSupport.park();//被阻塞...等待通知等待放行它要通过需要许可证 //1675241932025--没起作用--1675241932025LockSupport.park();//被阻塞...等待通知等待放行它要通过需要许可证 //1675241932025--没起作用--1675241932025System.out.println(Thread.currentThread().getName() \t -----被唤醒System.currentTimeMillis());}, a);a.start();Thread b new Thread(() - {LockSupport.unpark(a);LockSupport.unpark(a);//最多一个凭证System.out.println(Thread.currentThread().getName() \t ------通知);}, b);b.start();} } 15_AQS理论初步 4.AbstractQueuedSynchronizer之AQS 先从字节跳动及其它大厂面试题说起前置知识 同学反馈2020.6.27 【Java集合类】 1、从集合开始吧介绍一下常用的集合类哪些是有序的哪些是无序的 2、hashmap是如何寻址的哈希碰撞后是如何存储数据的1.8后什么时候变成红黑树、说下红黑树的理解红黑树有什么好处 3、concurrrenthashmap 怎么实现线程安全一个里面会有几个段 segmentjdk1.8后有优化concurrenthashmap吗?分段锁有什么坏处 【多线程JUC】 4、reentrantlock实现原理简单说下aqs 5、synchronized实现原理monitor对象什么时候生成的?知道monitor的monitorenter和moni这两个是怎么保证同步的吗或者说这两个操作讦算机底层是如何执行的 6、刚刚你提到了synchronized的优化过程详细说一下吧。偏向锁和轻量级锁有什么区别? 7、线程池几个参数说下你们项目中如何根据实际场景设詈参数的为什么cpu密集设置的线程密集型少 前置知识 公平锁和非公平锁可重入锁LockSupport自旋锁数据结构之链表设计模式之模板设计模式 是什么 字面意思 抽象的队列同步器源代码 技术解释 是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石通过内置的FIFO队列来完成资源获取线程的排队工作并通过一个int类型变量表示持有锁的状态。 CLHCraig、Landin and Hagersten队列是一个单向链表AQS中的队列是CLH变体的虚拟双向队列FIFO。 16_AQS能干嘛 AQS为什么是JUC内容中最重要的基石 和AQS有关的 ReentrantLock CountDownLatch ReentrantReadWriteLock Semaphore … 进一步理解锁和同步器的关系 锁面向锁的 使用者 ----定义了程序员和锁交互的使用层APl隐藏了实现细节你调用即可 同步器面向锁的 实现者 -----比如Java并发大神DougLee提出统一规范并简化了锁的实现屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等。 能干嘛 加锁会导致阻塞 有阻塞就需要排队实现排队必然需要有某种形式的队列来进行管理 解释说明 抢到资源的线程直接使用处理业务逻辑抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了暂时没有受理窗口的顾客只能去候客区排队等候)但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号轮到了再去受理窗口办理业务)。 既然说到了排队等候机制那么就一定会有某种队列形成这样的队列是什么数据结构呢? 如果共享资源被占用就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的将暂时获取不到锁的线程加入到队列中这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node)通过CAS、自旋以及LockSupport.park() 的方式维护state变量的状态使并发达到同步的控制效果。 17_AQS源码体系-上 AQS初步 AQS初识 官网解释 有阻塞就需要排队实现排队必然需要队列 AQS使用一个volatile的int类型的成员变量来表示同步状态通过内置的FIFo队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配通过CAS完成对State值的修改。 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {private static final long serialVersionUID 7373984972572414691L;* Creates a new {code AbstractQueuedSynchronizer} instanceprotected AbstractQueuedSynchronizer() { }* Wait queue node class.static final class Node {* Head of the wait queue, lazily initialized. Except forprivate transient volatile Node head;* Tail of the wait queue, lazily initialized. Modified only viaprivate transient volatile Node tail;* The synchronization state.private volatile int state;* Returns the current value of synchronization state.protected final int getState() {* Sets the value of synchronization state.protected final void setState(int newState) {* Atomically sets synchronization state to the given updatedprotected final boolean compareAndSetState(int expect, int update) {... } 18_AQS源码体系-下 AQS内部体系架构 AQS自身 AQS的int变量 AQS的同步状态state成员变量 state成员变量相当于银行办理业务的受理窗口状态。 零就是没人自由状态可以办理 大于等于1有人占用窗口等着去 AQS的CLH队列 CLH队列(三个大牛的名字组成)为一个双向队列 银行候客区的等待顾客 小总结 有阻塞就需要排队实现排队必然需要队列 state变量CLH变种的双端队列 AbstractQueuedSynchronizer内部类 Node的int变量 Node的等待状态waitState成员变量 说人话 等候区其它顾客(其它线程)的等待状态队列中每个排队的个体就是一个Node Node此类的讲解 内部结构 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...* Creates a new {code AbstractQueuedSynchronizer} instanceprotected AbstractQueuedSynchronizer() { }* Wait queue node class.static final class Node {//表示线程以共享的模式等待锁/** Marker to indicate a node is waiting in shared mode */static final Node SHARED new Node();//表示线程正在以独占的方式等待锁/** Marker to indicate a node is waiting in exclusive mode */static final Node EXCLUSIVE null;//线程被取消了/** waitStatus value to indicate thread has cancelled */static final int CANCELLED 1;//后继线程需要唤醒/** waitStatus value to indicate successors thread needs unparking */static final int SIGNAL -1;//等待condition唤醒/** waitStatus value to indicate thread is waiting on condition */static final int CONDITION -2;//共享式同步状态获取将会无条件地传播下去* waitStatus value to indicate the next acquireShared should static final int PROPAGATE -3;//当前节点在队列中的状态重点//说人话//等候区其它顾客(其它线程)的等待状态//队列中每个排队的个体就是一个Node//初始为0状态上面的几种* Status field, taking on only the values:volatile int waitStatus;//前驱节点重点* Link to predecessor node that current node/thread relies onvolatile Node prev;//后继节点重点* Link to the successor node that the current node/threadvolatile Node next;//表示处于该节点的线程* The thread that enqueued this node. Initialized onvolatile Thread thread;//指向下一个处于CONDITION状态的节点* Link to next node waiting on condition, or the specialNode nextWaiter;* Returns true if node is waiting in shared mode.final boolean isShared() {//返回前驱节点没有的话抛出npe* Returns previous node, or throws NullPointerException if null.final Node predecessor() throws NullPointerException {Node() { // Used to establish initial head or SHARED markerNode(Thread thread, Node mode) { // Used by addWaiterNode(Thread thread, int waitStatus) { // Used by Condition}... } 属性说明 AQS同步队列的基本结构 19_AQS源码深度解读01 从我们的ReentrantLock开始解读AQS Lock接口的实现类基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的 public class ReentrantLock implements Lock, java.io.Serializable {private static final long serialVersionUID 7373984872572414699L;/** Synchronizer providing all implementation mechanics */private final Sync sync;/*** Base of synchronization control for this lock. Subclassed* into fair and nonfair versions below. Uses AQS state to* represent the number of holds on the lock.*/abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID -5179523762034025860L;}public void lock() {sync.lock();}public void unlock() {sync.release(1);}ReentrantLock的原理 从最简单的lock方法开始看看公平和非公平 /*** Creates an instance of {code ReentrantLock}.* This is equivalent to using {code ReentrantLock(false)}.*/public ReentrantLock() {sync new NonfairSync();}/*** Creates an instance of {code ReentrantLock} with the* given fairness policy.** param fair {code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync fair ? new FairSync() : new NonfairSync();} 可以明显看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件hasQueuedPredecessors() hasQueuedPredecessors是公平锁加锁时判断等待队列中是否存在有效节点的方法 //公平锁加锁时判断等待队列中是否存在有效节点的方法public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t tail; // Read fields in reverse initialization orderNode h head;Node s;return h ! t ((s h.next) null || s.thread ! Thread.currentThread());// h ! t 等待队列非空// (s h.next) null 队首是否为空// s.thread ! Thread.currentThread()) 队首是否是当前线程// 判断等待队列是否是空或等待队列队首是否是当前线程} 非公平锁走起方法lock() 对比公平锁和非公平锁的tyAcquire()方法的实现代码其实差别就在于非公平锁获取锁时比公平锁中少了一个判断!hasQueuedPredecessors() hasQueuedPredecessors()中判断了是否需要排队导致公平锁和非公平锁的差异如下 公平锁公平锁讲究先来先到线程在获取锁时如果这个锁的等待队列中已经有线程在等待那么当前线程就会进入等待队列中; 非公平锁不管是否有等待队列如果可以获取锁则立刻占有锁对象。也就是说队列的第一个排队线程在unpark()之后还是需要竞争锁存在线程竞争的情况下) 本次讲解我们走最常用的lock/unlock作为案例突破口源码解读比较困难别着急—阳哥的全系列脑图给大家做好笔记AQS源码深度分析走起 整个 ReentrantLock的加锁过程可以分为三个阶段: 1、尝试加锁; 2、加锁失败线程入队列; 3、线程入队列后进入阻塞状态。 对应下面①②③三部分。 public final void acquire(int arg) {if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}20_AQS源码深度解读02 ReentrantLock的示例程序 带入一个银行办理业务的案例来模拟我们的AQS 如何进行线程的管理和通知唤醒机制3个线程模拟3个来银行网点受理窗口办理业务的顾客。 package juc;import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.TimeUnit;public class AQSDemo {public static void main(String[] args) {ReentrantLock lock new ReentrantLock();//带入一个银行办理业务的案例来模拟我们的AQs 如何进行线程的管理和通知唤醒机制\//3个线程模拟3个来银行网点受理窗口办理业务的顾客//A顾客就是第一个顾客此时受理窗口没有任何人A可以直接去办理new Thread(()-{lock.lock();try {System.out.println(Thread.currentThread().getName() come in.);try {TimeUnit.SECONDS.sleep(5);//模拟办理业务时间} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}, Thread A).start();//第2个顾客第2个线程----由于受理业务的窗口只有一个(只能一个线程持有锁)此代B只能等待//进入候客区new Thread(()-{lock.lock();try {System.out.println(Thread.currentThread().getName() come in.);} finally {lock.unlock();}}, Thread B).start();//第3个顾客第3个线程----由于受理业务的窗口只有一个(只能一个线程持有锁)此代C只能等待//进入候客区new Thread(()-{lock.lock();try {System.out.println(Thread.currentThread().getName() come in.);} finally {lock.unlock();}}, Thread C).start();} } 程序初始状态方便理解图 lock() acquire() tryAcquire(arg) addWaiter(Node.EXCLUSIVE) acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 启动程序首先是运行线程AReentrantLock默认是选用非公平锁。 public class ReentrantLock implements Lock, java.io.Serializable {...* Acquires the lock.public void lock() {sync.lock();//------------------------注意我们从这里入手,一开始将线程A的}abstract static class Sync extends AbstractQueuedSynchronizer {...//被NonfairSync的tryAcquire()调用final boolean nonfairTryAcquire(int acquires) {final Thread current Thread.currentThread();int c getState();if (c 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current getExclusiveOwnerThread()) {int nextc c acquires;if (nextc 0) // overflowthrow new Error(Maximum lock count exceeded);setState(nextc);return true;}return false;}...}//非公平锁static final class NonfairSync extends Sync {private static final long serialVersionUID 7316153563782823691L;/*** Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {//----线程A的lock.lock()调用该方法if (compareAndSetState(0, 1))//AbstractQueuedSynchronizer的方法,刚开始这方法返回truesetExclusiveOwnerThread(Thread.currentThread());//设置独占的所有者线程显然一开始是线程Aelseacquire(1);//稍后紧接着的线程B将会调用该方法。}//acquire()将会间接调用该方法protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);//调用父类Sync的nonfairTryAcquire()}}... } public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {/*** The synchronization state.*/private volatile int state;//线程A将state设为1下图红色椭圆区/*Atomically sets synchronization state to the given updated value if the current state value equals the expected value.This operation has memory semantics of a volatile read and write.*/protected final boolean compareAndSetState(int expect, int update) {// See below for intrinsics setup to support thisreturn unsafe.compareAndSwapInt(this, stateOffset, expect, update);}} 线程A开始办业务了。 B将会等候 acquire(1);//稍后紧接着的线程B将会调用该方法。调用acquire方法 public final void acquire(int arg) {if (!tryAcquire(arg) acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}21_AQS源码深度解读03 轮到线程B运行 public class ReentrantLock implements Lock, java.io.Serializable {...* Acquires the lock.public void lock() {sync.lock();//------------------------注意我们从这里入手,线程B的执行这}//非公平锁static final class NonfairSync extends Sync {private static final long serialVersionUID 7316153563782823691L;/*** Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {//-------------------------线程B的lock.lock()调用该方法if (compareAndSetState(0, 1))//这是预定线程A还在工作这里返回falsesetExclusiveOwnerThread(Thread.currentThread());//elseacquire(1);//线程B将会调用该方法该方法在AbstractQueuedSynchronizer//它会调用本类的tryAcquire()方法}//acquire()将会间接调用该方法protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);//调用父类Sync的nonfairTryAcquire()}}//非公平锁与公平锁的公共父类* Base of synchronization control for this lock. Subclassedabstract static class Sync extends AbstractQueuedSynchronizer {//acquire()将会间接调用该方法 但是返回值会取反...final boolean nonfairTryAcquire(int acquires) {final Thread current Thread.currentThread();//这里是线程Bint c getState();//线程A还在工作c1//这种情况就是持有锁的线程A刚刚释放锁了等待线程B就可以占用锁了if (c 0) {//falseif (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current);return true;}}//这种情况就是持有锁的线程还想占有锁 可重入锁else if (current getExclusiveOwnerThread()) {//(线程B 线程A) false int nextc c acquires;//1 if (nextc 0) // overflowthrow new Error(Maximum lock count exceeded);setState(nextc);return true;}return false;//最终返回false} ...}... } public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...* Acquires in exclusive mode, ignoring interrupts. Implementedpublic final void acquire(int arg) {if (!tryAcquire(arg) //线程B调用非公平锁的tryAcquire(), 最终返回false加上!,也就是true,也就是还要执行下面两行语句acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//下一节论述 Node.EXCLUSIVE排他的selfInterrupt();}... } 另外 假设线程BC还没启动正在工作线程A重新尝试获得锁也就是调用lock.lock()多一次 //非公平锁与公平锁的公共父类fa* Base of synchronization control for this lock. Subclassedabstract static class Sync extends AbstractQueuedSynchronizer {...final boolean nonfairTryAcquire(int acquires) {final Thread current Thread.currentThread();//这里是线程Aint c getState();//线程A还在工作c1如果线程A恰好运行到在这工作完了c0这时它又要申请锁的话if (c 0) {//线程A正在工作为false;如果线程A恰好工作完c0这时它又要申请锁的话,则为trueif (compareAndSetState(0, acquires)) {//线程A重新获得锁setExclusiveOwnerThread(current);//这里相当于NonfairSync.lock[添加链接描述](https://www.bilibili.com/video/BV1Hy4y1B78T?p23)()另一重设置吧return true;}}else if (current getExclusiveOwnerThread()) {//(线程A 线程A) trueint nextc c acquires;//11nextc2if (nextc 0) // overflowthrow new Error(Maximum lock count exceeded);setState(nextc);//state2,说明要unlock多两次吧现在盲猜return true;//返回true}return false;} ...} 22_AQS源码深度解读04 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...* Acquires in exclusive mode, ignoring interrupts. Implementedpublic final void acquire(int arg) {if (!tryAcquire(arg) //线程B调用非公平锁的tryAcquire(), 最终返回false加上!,也就是true,也就是还要执行下面两行语句acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//线程B加入等待队列selfInterrupt();//下一节论述}private Node addWaiter(Node mode) {Node node new Node(Thread.currentThread(), mode); //此时是线程B 模式是排他 此时等候队列无结点// Try the fast path of enq; backup to full enq on failureNode pred tail;if (pred ! null) {//根据上面一句注释本语句块的意义是将新节点快速添加至队尾node.prev pred;if (compareAndSetTail(pred, node)) {pred.next node;return node;}}enq(node);//快速添加至队尾失败则用这方法调用可能链表为空才调用该方法return node;}//Inserts node into queue, initializing if necessary.private Node enq(final Node node) {for (;;) { Node t tail;//t上一次循环的尾指针也就是虚拟头结点if (t null) { // Must initialize 此时尾指针为空if (compareAndSetHead(new Node()))//插入一个哨兵节点或称傀儡节点 头指针--虚拟头结点tail head; //尾指针--虚拟头结点} else { //循环第二次走以下代码node.prev t; //虚拟头结点t---Bif (compareAndSetTail(t, node)) {//真正插入我们需要的节点也就是包含线程B引用的节点 尾指针指向tail---B结点t.next node; //虚拟头结点t---Breturn t;}}}}//CAS head field. Used only by enq.private final boolean compareAndSetHead(Node update) {return unsafe.compareAndSwapObject(this, headOffset, null, update);}//CAS tail field. Used only by enq.private final boolean compareAndSetTail(Node expect, Node update) {return unsafe.compareAndSwapObject(this, tailOffset, expect, update);}... } 线程B加入等待队列。 23_AQS源码深度解读05 线程A依然工作线程C如线程B那样炮制加入等待队列。 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...* Acquires in exclusive mode, ignoring interrupts. Implementedpublic final void acquire(int arg) {if (!tryAcquire(arg) //线程C调用非公平锁的tryAcquire(), 最终返回false加上!,也就是true,也就是还要执行下面两行语句acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//线程C加入等待队列selfInterrupt();//下一节论述}private Node addWaiter(Node mode) {Node node new Node(Thread.currentThread(), mode); //此时是线程C 模式是排他 此时等候队列有虚拟头结点 结点B// Try the fast path of enq; backup to full enq on failureNode pred tail;//此时前驱结点pred尾结点tailB//将新节点快速添加至队尾if (pred ! null) {//B!nullnode.prev pred; //B---Cif (compareAndSetTail(pred, node)) {//尾指针tail---Cpred.next node;//前驱结点B---Creturn node;//返回}}enq(node);//快速添加至队尾失败则用这方法调用可能链表为空才调用该方法return node;}... } acquire() ----源码和三大流程 tryAcquire(arg)-----本次走非公平锁 -----nonfairTryAcquire(acquires) true-----继续推进条件走下一个方法addWaiter false-----结束 addWaiter(Node.EXCLUSIVE) addwaiter (Node mode) enq(node);双向链表中第一个节点为虚节点(也叫哨兵节点)其实并不存储任何信息只是占位。真正的第一个有数据的节点是从第二个节点开始的。 假如3号Threadc线程进来 prevcompareAndSetTailnext 24_AQS源码深度解读06 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) acquireQueued 假如再抢抢失败就会进入 shouldParkAfterFailedAcquire和 parkAndChecklnterrupt方法中 shouldParkAfterFailedAcquire 如果前驱节点的 waitStatus是SIGNAL状态即 shouldParkAfterFailedAcquire方法会返回true程序会继续向下执行parkAndCheckInterrupt方法用于将当前线程挂起 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//获取前驱结点的状态int ws pred.waitStatus;//如果是SIGNAL状态即等待被占用的资源释放直接返回 true//准备继续调用 parkAndCheckInterrupt方法if (ws Node.SIGNAL)return true;//ws大于0说明是CANCELLED状态,if (ws 0) {//跳过//循环判断前驱节点的前驱节点是否也为CANCELLED状态忽略该状态的节点重新连接队列do {node.prev pred pred.prev;} while (pred.waitStatus 0);pred.next node;} else {//将当前节点的前驱节点设置为设置为SIGNAL状态用于后续唤醒操作//程序第一次执行到这返回为false还会进行外层第二次循环最终从代码第7行返回compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}parkAndCheckInterrupt 程序测试 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...* Acquires in exclusive mode, ignoring interrupts. Implementedpublic final void acquire(int arg) {if (!tryAcquire(arg) //线程B调用非公平锁的tryAcquire(), 最终返回false加上!,也就是true,也就是还要执行下面两行语句//线程B加入等待队列acquireQueued本节论述--------------------------acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();//}//Acquires in exclusive uninterruptible mode for thread already inqueue. //Used by condition wait methods as well as acquire.////return true if interrupted while waitingfinal boolean acquireQueued(final Node node, int arg) {boolean failed true;//是否被取消try {boolean interrupted false//是否被打断for (;;) {final Node p node.predecessor();//1.返回前驱节点对与线程B来说p也就是傀儡节点 对线程C的前驱结点是B//phead为truetryAcquire()方法说明请转至 #21_AQS源码深度解读03//假设线程A正在工作,现在线程B只能等待所以tryAcquire(arg)返回false下面的if语块不执行////第二次循环假设线程A继续正在工作下面的if语块还是不执行 对于C线程 进入不了if (p head tryAcquire(arg)) {setHead(node);p.next null; // help GCfailed false;return interrupted;}//请移步到2.处的shouldParkAfterFailedAcquire()解说。第一次返回false, 下一次第二次循环//第二次循环shouldParkAfterFailedAcquire()返回true执行parkAndCheckInterrupt()if (shouldParkAfterFailedAcquire(p, node) //4. parkAndCheckInterrupt())//park导致BC阻塞到这方法里了interrupted true;//原来一直想tryAcquire现在正在阻塞了在候客区了}} finally {if (failed)cancelAcquire(node);//取消排队}}static final class Node {...//1.返回前一节点final Node predecessor() throws NullPointerException {Node p prev;if (p null)throw new NullPointerException();elsereturn p;}...}//2. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws pred.waitStatus;//此时pred指向傀儡节点它的waitStatus为0//Node.SIGNAL为-1跳过//第二次调用ws为-1条件成立返回trueif (ws Node.SIGNAL)//-1/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;if (ws 0) {//跳过/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*/do {node.prev pred pred.prev;} while (pred.waitStatus 0);pred.next node;} else {/** waitStatus must be 0 or PROPAGATE. Indicate that we* need a signal, but dont park yet. Caller will need to* retry to make sure it cannot acquire before parking.*///3. 傀儡节点的WaitStatus设置为-1//下图红圈compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;//第一次返回}/*** CAS waitStatus field of a node.*///3.private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);}/*** Convenience method to park and then check if interrupted** return {code true} if interrupted*///4.private final boolean parkAndCheckInterrupt() {//前段章节讲述的LockSupportthis指的是NonfairSync对象//这意味着真正阻塞线程B同样地阻塞了线程CLockSupport.park(this);//线程B,C在此处暂停了运行-------------------------return Thread.interrupted();}} 图中的B结点把傀儡节点的waitStatus由0变为-1Node.SIGNAL。 C结点会把B结点的waitStatus由0变为-1Node.SIGNAL。 25_AQS源码深度解读07 接下来讨论ReentrantLock.unLock()方法。假设线程A工作结束调用unLock()释放锁占用。 方法unlock() sync.release(1); tryRelease(int arg); unparkSuccessor 杀回马枪 public class ReentrantLock implements Lock, java.io.Serializable {private final Sync sync;abstract static class Sync extends AbstractQueuedSynchronizer {...//2.unlock()间接调用本方法releases传入1protected final boolean tryRelease(int releases) {//3.int c getState() - releases;//c为0//4.if (Thread.currentThread() ! getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free false;if (c 0) {//c为0条件为ture执行if语句块free true;//5.setExclusiveOwnerThread(null);}//6.setState(c);//设置状态为0return free;//最后返回true}...}static final class NonfairSync extends Sync {...}public ReentrantLock() {sync new NonfairSync();//我们使用的非公平锁}//注意注意注意public void unlock() {//----------从这开始假设线程A工作结束调用unLock()释放锁占用//1.sync.release(1);//在AbstractQueuedSynchronizer类定义}...} public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {...//1.public final boolean release(int arg) {//2.if (tryRelease(arg)) {//该方法看子类NonfairSync实现最后返回trueNode h head;//返回傀儡节点if (h ! null h.waitStatus ! 0)//傀儡节点非空且状态为-1条件为true执行if语句//7.unparkSuccessor(h);return true;}return false;//返回true,false都无所谓了unlock方法只是简单调用release方法对返回结果没要求}/*** The synchronization state.*/private volatile int state;//3.protected final int getState() {return state;}//6.protected final void setState(int newState) {state newState;}//7. Wakes up nodes successor, if one exists.//传入傀儡节点private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling. It is OK if this* fails or if status is changed by waiting thread.*/int ws node.waitStatus;//傀儡节点waitStatus为-1if (ws 0)//ws为-1条件成立执行if语块compareAndSetWaitStatus(node, ws, 0);//8.将傀儡节点waitStatus由-1变为0/** Thread to unpark is held in successor, which is normally* just the next node. But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/Node s node.next;//傀儡节点的下一节点,也就是带有线程B的节点if (s null || s.waitStatus 0) {//s非空s.waitStatus非0条件为false不执行if语块s null;for (Node t tail; t ! null t ! node; t t.prev)if (t.waitStatus 0)s t;}if (s ! null)//s非空条件为true执行if语块LockSupport.unpark(s.thread);//唤醒线程B。运行到这里线程A的工作基本告一段落了。}//8.private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);}} public abstract class AbstractOwnableSynchronizerimplements java.io.Serializable {...protected AbstractOwnableSynchronizer() { }private transient Thread exclusiveOwnerThread;//5.protected final void setExclusiveOwnerThread(Thread thread) {exclusiveOwnerThread thread;}//4.protected final Thread getExclusiveOwnerThread() {return exclusiveOwnerThread;} } 线程A结束工作调用unlock()的tryRelease()后的状态state由1变为0exclusiveOwnerThread由线程A变为null。 线程B被唤醒即从原先park()的方法继续运行 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {private final boolean parkAndCheckInterrupt() {LockSupport.park(this);//线程B从阻塞到非阻塞继续执行return Thread.interrupted();//线程B没有被中断返回false}...//Acquires in exclusive uninterruptible mode for thread already inqueue. //Used by condition wait methods as well as acquire.////return true if interrupted while waitingfinal boolean acquireQueued(final Node node, int arg) {boolean failed true;try {boolean interrupted false;for (;;) {final Node p node.predecessor();//线程B所在的节点的前一节点是傀儡节点//傀儡节点是头节点tryAcquire()的说明请移步至#21_AQS源码深度解读03//tryAcquire()返回true,线程B成功上位if (p head tryAcquire(arg)) {setHead(node);//1.将附带线程B的节点的变成新的傀儡节点p.next null; // help GC//置空原傀儡指针与新的傀儡节点之间的前后驱指针方便GC回收failed false;return interrupted;//返回false跳到2.acquire()}if (shouldParkAfterFailedAcquire(p, node) //唤醒线程B继续工作parkAndCheckInterrupt()返回false//if语块不执行跳到下一循环parkAndCheckInterrupt())//---------------------------------唤醒线程在这里继续运行interrupted true;}} finally {if (failed)cancelAcquire(node);}}//1. private void setHead(Node node) {head node;node.thread null;node.prev null;}//2.* Acquires in exclusive mode, ignoring interrupts. Implementedpublic final void acquire(int arg) {if (!tryAcquire(arg) //acquireQueued()返回fasle,条件为falseif语块不执行acquire()返回//也就是说线程B成功获得锁可以展开线程B自己的工作了。acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();//}} 最后线程B上位成功。 26_AQS小总结 aqs脑图 最后 2023-2-2 17:12:40 这篇博客能写好的原因是站在巨人的肩膀上 这篇博客要写好的目的是做别人的肩膀 开源为爱发电 学习为我而行
http://www.dnsts.com.cn/news/102051.html

相关文章:

  • 网站源代码程序广州新际网站建设
  • 襄阳营销型网站佛山哪有网站建设公司
  • 前程无忧做网站多少钱学做预算网站
  • 同城分类信息网站建设茶叶手机网站建设
  • 平台网站开发是什么意思山东手机版建站系统信息
  • 昆山网站建设第一品牌自定义投票网站怎么做
  • 宁波市网站建设图片制作gif
  • 网站安全检测网站建设开发人员须知
  • 建站快车官网wordpress闭站
  • 文山网站建设兼职类似于百度快照的网站
  • 营销类的网站在线建站网页制作网站建设平台
  • 闵行网站开发如何做流量网站
  • 好的网站你们会感谢我的新手小白怎么学做运营
  • 网站建设公司怎么开拓业务logo设计说明模板100字
  • 中济建设官方网站dede 网站名称
  • 深圳做品牌网站wordpress 搜索不能用
  • 《高性能网站建设指南网站正在建设中网页
  • 什么是wap网站国内seo排名
  • h5网站搭建教育企业网站源码
  • 郑州做网站比较好公司专门做旅游的网站
  • 028网站建设深圳网站程序开发
  • 扬州建设网站wordpress云采集
  • 外链网站 风险网站设计要点 优帮云
  • 做网络作家哪个网站好网站建设十年杜绝模板
  • e福州官方网站网络推广网站电话
  • 网站关键词分布图书馆建设投稿网站
  • 俄罗斯视频网站开发完成门户网站建设
  • 网站运营编辑做什么的wordpress搬家后台还是老网站
  • 福州网站开发si7.cc上海技术公司做网站
  • 写建设网站的具体步骤广州的网站建设公司哪家好