ToB企服应用市场:ToB评测及商务社交产业平台

标题: JUC锁:核心类AQS源码详解 [打印本页]

作者: 缠丝猫    时间: 2022-9-16 18:19
标题: JUC锁:核心类AQS源码详解
目录

1 疑点todo和解疑

同步状态变量:state就是那个共享资源(private volatile int state;) Lock类继承AQS类并定义lock()、unLock()的方法,表示获取锁和释放锁。多线程并发访问同一个lock实例,lock()方法会cas修改state变量,修改成功的线程获得锁,其他线程进入AQS队列等待。

没有必要!sync队列是双向链表结构,出队时,head交替方式,只需要修改head和head后继2个节点引用关系;固定head,就要修改head,head后继,以及head后继的后继 共3个节点。显然前者效率更高


不存在的,因为经过判断得出此时node就是head的后继。并且必须由这个取消节点node来唤醒后继,要不node线程结束后,就没有线程能够唤醒队列里的其他节点了。

先说结果:由抢到锁的那个线程来唤醒!
上述的场景是存在的,例如在非公平锁模式中,B线程被A线程唤醒,A结束,B成为head,B去执行tryAcquire(),但此时C线程抢占到锁,B执行tryAcquire()没有拿到锁,再次park阻塞。C线程执行结束后将A唤醒

只有将前置节点状态改为SIGNAL,才能确保当前节点可以被前置unPark唤醒。也就是说阻塞自己前先保证一定能够被唤醒。因为代码中:
独占模式下,唤醒后继前先限制:h.waitStatus != 0
共享模式下,唤醒后继前先限制:h.waitStatus=SIGNAL

表示本线程在获取资源期间,如果被其他线程中断,本线程不会因为中断而取消获取资源,只是将中断标记传递下去。

  1. When acquired in exclusive mode,
  2. * attempted acquires by other threads cannot succeed. Shared mode
  3. * acquires by multiple threads may (but need not) succeed. This class
  4. * does not "understand" these differences except in the
  5. * mechanical sense that when a shared mode acquire succeeds, the next
  6. * waiting thread (if one exists) must also determine whether it can
  7. * acquire as well. Threads waiting in the different modes share the
  8. * same FIFO queue.
复制代码

A、B先后进入队列,状态都是0。A获得资源,进入setHeadAndPropagate晋升为head,A进入doReleaseShared尝试唤醒B时,但B还没将A改为signal,因为A还是0,A将状态改为PROPAGATE
2 AbstractQueuedSynchronizer学习总结

2.1 AQS要点总结

对于AbstractQueuedSynchronizer的分析,最核心的就是sync queue的分析。
2.2 细节分析

2.2.1 插入节点时先更新prev再更新前驱next
  1. //addWaiter():
  2. node.prev = pred; // 1 更新node节点的prev域
  3. if (compareAndSetTail(pred, node)) {
  4.     pred.next = node; //2 更新node前驱的next域
  5.     return node;
  6. }
  7. //enq():
  8. node.prev = t; // 1 更新node节点的prev域
  9. if (compareAndSetTail(t, node)) {
  10.     t.next = node;//2 更新node前驱的next域
  11.     return t;
  12. }
  13. //unparkSuccessor():
  14. Node s = node.next; //通过.next来直接获取到节点的后继节点,这个节点的后继的prev一定指向节点本身
  15.       //....
  16.         if (s != null)
  17.             LockSupport.unpark(s.thread);
复制代码
2.2.2 为什么unparkSuccessor()要从尾部往前遍历

因为取消节点的next域指向了自身,所以不能从通过next来遍历,但prev是完整的,所以通过prev来遍历。
2.2.3  AQS的设计,尽快唤醒其他等待线程体现在3个地方

3 AQS 简介

AQS是一个用来构建锁和同步器的框架。理论参考:JUC同步器框架
三个基本组件相互协作:
同步器一般包含两种方法,一种是acquire,另一种是release。acquire操作阻塞调用的线程,直到或除非同步状态允许其继续执行。而release操作则是通过某种方式改变同步状态,使得一或多个被acquire阻塞的线程继续执行。
3.1 AQS核心思想

3.2 对资源的共享方式

两种方式:
3.3 AQS数据结构

分析类,首先就要分析底层采用了何种数据结构,抓住核心点进行分析:

4 AbstractQueuedSynchronizer源码分析

4.1 类的继承关系
  1. public abstract class AbstractQueuedSynchronizer
  2.     extends AbstractOwnableSynchronizer
  3.     implements java.io.Serializable
复制代码
继承自抽象类:AbstractOwnableSynchronizer,父类提供独占线程的设置与获取的方法
  1. public abstract class AbstractOwnableSynchronizer
  2.     implements java.io.Serializable {
  3.     private static final long serialVersionUID = 3737899427754241961L;
  4.     protected AbstractOwnableSynchronizer() { }//   构造函数
  5.     private transient Thread exclusiveOwnerThread; //独占模式下的线程
  6.     // 设置独占线程
  7.     protected final void setExclusiveOwnerThread(Thread thread) {
  8.         exclusiveOwnerThread = thread;
  9.     }
  10.     // 获取独占线程
  11.     protected final Thread getExclusiveOwnerThread() {
  12.         return exclusiveOwnerThread;
  13.     }
  14. }
复制代码
4.1.1 AQS需要子类重写的方法
  1.     protected boolean tryAcquire(int arg) {//独占方式获取锁
  2.         throw new UnsupportedOperationException();
  3.     }
  4.     protected boolean tryRelease(int arg) { //释放独占的锁
  5.         throw new UnsupportedOperationException();
  6.     }
  7.     protected int tryAcquireShared(int arg) { //以共享方式获取锁
  8.         throw new UnsupportedOperationException();
  9.     }
  10.     protected boolean tryReleaseShared(int arg) {//释放共享锁
  11.         throw new UnsupportedOperationException();
  12.     }
  13.     protected boolean isHeldExclusively() {//是否独占资源
  14.         throw new UnsupportedOperationException();
  15.     }
复制代码
关于重写说明
目的是将共享资源state的读写交给子类管理,AQS专注在队列的维护以及线程的阻塞与唤醒
4.2 类的常量/成员变量
  1. public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
  2.     implements java.io.Serializable {   
  3.     private static final long serialVersionUID = 7373984972572414691L;   
  4.     // 头节点
  5.     private transient volatile Node head;   
  6.     // 尾节点
  7.     private transient volatile Node tail;   
  8.     //0:表示没有线程获取到锁;1表示有线程获取到锁;大于1:表示有线程获得了锁,且允许重入
  9.     private volatile int state;   
  10.     // 自旋时间
  11.     static final long spinForTimeoutThreshold = 1000L;
  12.    
  13.     // 以下跟cas有关
  14.     private static final Unsafe unsafe = Unsafe.getUnsafe();  // Unsafe类实例
  15.     private static final long stateOffset; // state内存偏移地址
  16.     private static final long headOffset; // head内存偏移地址
  17.     private static final long tailOffset; // state内存偏移地址
  18.     private static final long waitStatusOffset;// tail内存偏移地址
  19.     private static final long nextOffset; // next内存偏移地址
  20.     // 静态初始化块
  21.     static {
  22.         try {
  23.             stateOffset = unsafe.objectFieldOffset
  24.                 (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  25.             headOffset = unsafe.objectFieldOffset
  26.                 (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  27.             tailOffset = unsafe.objectFieldOffset
  28.                 (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  29.             waitStatusOffset = unsafe.objectFieldOffset
  30.                 (Node.class.getDeclaredField("waitStatus"));
  31.             nextOffset = unsafe.objectFieldOffset
  32.                 (Node.class.getDeclaredField("next"));
  33.         } catch (Exception ex) { throw new Error(ex); }
  34.     }
  35. }
复制代码
说明:
4.3 静态内部类Node

线程封装成Node并具备状态
  1. static final class Node
  2. {
  3.         // 模式,分为共享与独占
  4.         static final Node SHARED = new Node();// 共享模式
  5.         static final Node EXCLUSIVE = null; // 独占模式
  6.         // 节点状态
  7.         static final int CANCELLED =  1;//表示当前的线程被取消
  8.         static final int SIGNAL    = -1;//表示当前节点的后继节点包含的线程需要被运行【被unpark】,
  9.         static final int CONDITION = -2;//表示当前节点在等待condition,也就是在condition队列中
  10.         static final int PROPAGATE = -3;//表示当前场景下后续的acquireShared能够得以执行
  11.         volatile int waitStatus;//节点状态;表示当前节点在sync队列中,等待着获取锁
  12.         volatile Node prev; // 指向当前节点的前驱
  13.         volatile Node next;// 指向当前节点的后继
  14.         volatile Thread thread;//节点所对应的线程
  15.         Node nextWaiter;// 下一个等待者    只跟condition有关
  16.     private transient volatile Node head; // 头节点  懒加载
  17.     private transient volatile Node tail; //尾节点  懒加载
  18.     private volatile int state;  // 同步状态
  19.         // 节点是否在共享模式下等待
  20.         final boolean isShared() {
  21.             return nextWaiter == SHARED;
  22.         }
  23.         // 获取前驱节点,若前驱节点为空,抛出异常
  24.         final Node predecessor() throws NullPointerException {
  25.             Node p = prev;// 保存前驱节点
  26.             if (p == null)
  27.                 throw new NullPointerException();
  28.             else
  29.                 return p;
  30.         }
  31.         // 无参构造函数
  32.         Node() { // Used to establish initial head or SHARED marker
  33.         }
  34.         // 构造函数
  35.         Node(Thread thread, Node mode) {        // Used by addWaiter
  36.             this.nextWaiter = mode;
  37.             this.thread = thread;
  38.         }
  39.         Node(Thread thread, int waitStatus) { // Used by Condition
  40.             this.waitStatus = waitStatus;
  41.             this.thread = thread;
  42.         }
  43. }
复制代码
关于Node说明
每个被阻塞的线程都会被封装成一个Node节点,放入队列。Node包含了一个Thread类型的引用,并且有自己的状态:
4.4 构造函数
  1. protected AbstractQueuedSynchronizer() { }    //默认的无参构造
复制代码
4.5 核心方法分析

4.5.1 核心方法概览
  1. public final void acquireShared(int arg) {...} // 获取共享资源的入口(忽略中断)
  2. protected int tryAcquireShared(int arg); // 尝试获取共享资源
  3. private void doAcquireShared(int arg) {...} // AQS中获取共享资源流程整合
  4. private Node addWaiter(Node mode){...} // 将node加入到同步队列的尾部
  5. protected int tryAcquireShared(int arg); // 尝试获取共享资源
  6. private void setHeadAndPropagate(Node node, int propagate) {...} // 设置 同步队列的head节点,以及触发"传播"操作
  7. private void doReleaseShared() {...} // 遍历同步队列,调整节点状态,唤醒待申请节点
  8. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {...} // 如果获取资源失败,则整理队中节点状态,并判断是否要将线程挂起
  9. private final boolean parkAndCheckInterrupt() {...} // 将线程挂起,并在挂起被唤醒后检查是否要中断线程(返回是否中断)
  10. private void cancelAcquire(Node node) {...} // 取消当前节点获取资源,将其从同步队列中移除
复制代码
4.5.2 acquire()方法

该函数以独占模式获取(资源),忽略中断
流程如下:

源码如下:
  1. public final void acquire(int arg) {
  2.     if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  3.         selfInterrupt(); //来到这里,表示线程拿到锁,并且读取到线程的中断标识为true,调用selfInterrupt()来恢复线程的interrupted中断标志(被parkAndCheckInterrupt()擦除了,所以再设置一次)。
  4. }
  5. static void selfInterrupt() {
  6.     Thread.currentThread().interrupt();//线程设置interrupted中断标志
  7. }
  8. protected boolean tryAcquire(int arg) {
  9.         throw new UnsupportedOperationException();
  10.     }
复制代码
acquire()总结
4.5.3 addWaiter()方法

addWaiter:快速添加的方式往sync queue尾部添加节点
  1. // 添加等待者
  2.     private Node addWaiter(Node mode) {
  3.         // 新生成一个节点
  4.         Node node = new Node(Thread.currentThread(), mode);
  5.         // 创建临时引用pred,跟tail指向相同地址
  6.         Node pred = tail;
  7.         if (pred != null) { // 尾节点不为空,即队列已经初始化过
  8.             // 将node的prev域连接到尾节点
  9.             node.prev = pred;
  10.             if (compareAndSetTail(pred, node)) { // cas更新tail,指向新创建的node
  11.                 // 设置尾节点的next域为node
  12.                 pred.next = node;  // 结合 node.prev = pred;  形成双向链表
  13.                 return node; // 返回新生成的节点
  14.             }
  15.         }
  16.         enq(node); // 队列还未初始化,或者是compareAndSetTail操作失败,则进入enq
  17.         return node;
  18.     }
  19.     //关于并发情景说明:
  20.     // 从 Node pred = tail;  到 compareAndSetTail(pred, node); 期间,队列可能插入了新的节点,pred指向的不是最新的tail,那么compareAndSetTail(pred, node) 就会执行失败,同时 node.prev = pred; node的前驱也不是最新的tail。
  21.     // 通过enq()来解决并发问题,enq()通过自旋+cas来保证线程安全
复制代码
addWaiter()说明
4.5.4 enq()方法
  1.     // 线程安全地创建队列、或者将节点插入队列、
  2.     private Node enq(final Node node) {
  3.         for (;;) { // 自旋+cas,确保节点能够成功入队列
  4.             Node t = tail;//尾节点
  5.             if (t == null) { // 尾节点为空,即还没被初始化
  6.                 if (compareAndSetHead(new Node())) // 设置head。 !!!!注意,这里是new node,没有使用参数的node,因此head节点不引用任何线程
  7.                     tail = head; // 头节点与尾节点都指向同一个新生节点。循环继续,进入else后,参数node将插入到队列
  8.             } else { // 尾节点不为空,即已经被初始化过
  9.                 node.prev = t;  // 将node节点的prev域连接到尾节点
  10.                 if (compareAndSetTail(t, node)) { // 比较更新tail,node成为新的tail
  11.                     // 设置尾节点的next域为node
  12.                     t.next = node;   // 结合 node.prev = t;   形成双向链表
  13.                     return t; // 返回Node的前驱节点
  14.                 }
  15.             }
  16.         }
  17.     }
  18.    
  19.     //CAS head field. Used only by enq.
  20.     private final boolean compareAndSetHead(Node update) {
  21.         return unsafe.compareAndSwapObject(this, headOffset, null, update);
  22.     }
  23.     //CAS head field. Used only by enq.
  24.     private final boolean compareAndSetTail(Node expect, Node update) {
  25.         return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
  26.     }
复制代码
enq()方法总结:
4.5.5 acquireQueue()方法

作用:sync队列中的节点在独占且忽略中断的模式下获取(资源)
源码如下:
  1. // sync队列中的节点在独占且忽略中断的模式下获取(资源):
  2.     final boolean acquireQueued(final Node node, int arg) {
  3.         // 标志
  4.         boolean failed = true;
  5.         try {
  6.             // 中断标识。如果线程唤醒后,中断标识是true,外层的acquire()将进入selfInterrupt()。
  7.             boolean interrupted = false;
  8.             // 无限循环 :如果前驱不是head,那线程将park阻塞,等待前面的节点依次执行,直到被unPark唤醒
  9.             for (;;) {
  10.                 // 获取node的前驱,如果前驱是head,则表明前面已经没有线程等待了,该线程可能成为工作线程
  11.                 final Node p = node.predecessor();
  12.                 // 前驱为头节点并且成功获得锁
  13.                 if (p == head && tryAcquire(arg)) {
  14.                     setHead(node); // node晋升为head
  15.                     p.next = null; // 旧head的next域指向null,将会被GC,移出队列
  16.                   
  17.                    failed = false; // 设置标志
  18.                     return interrupted; //拿到锁,break循环,并返回中断标识
  19.                 }
  20.                 //执行到这里,前驱非head 或者 前驱是head但获取锁失败,那么:1、将前驱状态改为signal 2、当前线程unPark阻塞
  21.                 //shouldParkAfterFailedAcquire():寻找非取消状态的前驱,如果状态为signal返回true 反则,将前驱状态改为signal、再返回false
  22.                 //前驱是signal ,执行parkAndCheckInterrupt()后,当前线程park阻塞。一直到线程被unPark唤醒,再返回线程的中断状态
  23.                 if (shouldParkAfterFailedAcquire(p, node) &&
  24.                     parkAndCheckInterrupt())//parkAndCheckInterrupt返回true表明线程中断状态为true
  25.                    //上面if同时成立,才会执行。
  26.                    interrupted = true;  //那么把中断标识置为true
  27.             }
  28.         } finally { //(有异常,在抛出之前执行finally;没有异常,在return之前执行finally)
  29.             if (failed)//只有try的代码块出现异常,failed才会是true。什么情景会产生异常?cancelAcquire分析时有说明
  30.                 cancelAcquire(node); //执行取消逻辑
  31.         }
  32.     }
  33.    
  34.     final Node predecessor() throws NullPointerException {
  35.             Node p = prev;
  36.             if (p == null)
  37.                 throw new NullPointerException();
  38.             else
  39.                 return p;
  40.         }
  41.    
  42.       private void setHead(Node node) {
  43.         head = node;
  44.         node.thread = null;//再次表明head的thread属性是空的
  45.         node.prev = null;
  46.     }
复制代码
acquireQueue()总结
4.5.6 shouldParkAfterFailedAcquire()方法
  1. // 当获取(资源)失败后:1、判断能否将当前线程park;2、修改前驱节点状态为signal
  2.     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
  3.         // 获取前驱节点的状态
  4.         int ws = pred.waitStatus;
  5.         if (ws == Node.SIGNAL) // 状态为SIGNAL
  6.             // 只有当前驱节点为 signal时,才返回true ,表示当前线程可以安全地park阻塞;其它情况返回false
  7.             return true;
  8.             //跳过那些CANCELLED状态的前驱
  9.         if (ws > 0) { // 表示状态为CANCELLED,为1
  10.             do {
  11.                 node.prev = pred = pred.prev;
  12.             } while (pred.waitStatus > 0); // 找到pred节点前面最近的一个状态不为CANCELLED的节点;然后跳出循环并返回false
  13.             pred.next = node;
  14.         } else { // 为PROPAGATE -3 或者是0 ,(为CONDITION -2时,表示此节点在condition queue中)
  15.              // cas更新前驱的状态为SIGNAL.如果前驱是头节点,那么头节点ws=SIGNAL
  16.             compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
  17.         }
  18.         // 不能进行park操作
  19.         return false;
  20.     }
  21.     //CAS waitStatus field of a node.
  22.     private static final boolean compareAndSetWaitStatus(Node node,int expect,  int update) {
  23.         return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
  24.     }
复制代码
shouldParkAfterFailedAcquire()总结:
4.5.7 parkAndCheckInterrupt()方法
  1. // 进行park操作并且返回该线程的中断标识
  2.     private final boolean parkAndCheckInterrupt() {
  3.         LockSupport.park(this); //外面的for循环可能会导致多次park,不过没关系,park允许多次执行
  4.        //被唤醒之后,返回中断标记,即如果是正常唤醒则返回false,如果是由于中断醒来,就返回true
  5.         return Thread.interrupted(); // acquireQueued() 中声明的interrupted 将会被更新为这里的返回结果
  6.     }
  7.     public static boolean interrupted() {
  8.         return currentThread().isInterrupted(true);//返回当前线程interrupted中断标记,同时会清除此interrupted标记
  9.     }
复制代码
方法总结:
4.5.8 cancelAcquire()方法

什么时候才会执行cancelAcquire?
  1. 在lockInterruptibly()会通过抛出中断异常来执行cancelAcquire方法,lock方法过程中则不会执行该代码,作者这么些的意图在于for循环内部如果出现不可控的因素导致产生未知的异常,则会执行cancelAcquire,很明显这属于一种相对偏保守的保险代码。
复制代码
  1. // 取消获取锁
  2.     private void cancelAcquire(Node node) {
  3.         // Ignore if node doesn't exist
  4.         if (node == null) // node为空,返回
  5.             return;
  6.         node.thread = null;// thread置空 备注1
  7.         // Skip cancelled predecessors
  8.         Node pred = node.prev;// pred表示:最靠近node并且状态不等于取消的前驱节点
  9.         while (pred.waitStatus > 0)
  10.             node.prev = pred = pred.prev; //更新pred,往列头推进
  11.             
  12.         Node predNext = pred.next; //predNext表示:pred的后继
  13.         // 设置node节点的状态为CANCELLED
  14.         node.waitStatus = Node.CANCELLED; //备注2
  15.         if (node == tail && compareAndSetTail(node, pred)) { // 若node节点为尾节点,则pred成为尾节点  备注3
  16.             // pred的next域置为null
  17.             compareAndSetNext(pred, predNext, null);
  18.         } else { // 2、node节点不为尾节点,或者比较设置不成功
  19.             int ws;
  20.             //下面一串判断,最终目标:在node移除队列前,将有效的前驱节点状态改为signal
  21.             if (pred != head &&
  22.                 ((ws = pred.waitStatus) == Node.SIGNAL ||
  23.                  (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
  24.                 pred.thread != null) {
  25.                 // pred节点不为头节点,并且
  26.                     //pred节点的状态为SIGNAL)或者
  27.                       // pred节点状态小于等于0,并且比较并设置等待状态为SIGNAL成功,并且pred节点所封装的线程不为空
  28.                 Node next = node.next;
  29.                 if (next != null && next.waitStatus <= 0) // 后继不为空并且后继的状态小于等于0
  30.                     compareAndSetNext(pred, predNext, next); // 比较并设置pred.next = next;  到这里:node的前驱节点指向node的后继节点。 备注4
  31.             } else {
  32.             // 这里,pred==head (3、即node是head的后继)或者pred.status=0,-2时 【前面while (pred.waitStatus > 0) 已经限制了pred一定是<=0】,执行:
  33.                 unparkSuccessor(node); // 唤醒node的后继
  34.             }
  35.             node.next = node; // help GC  后继节点指向自身  备注5
  36.         }
  37.     }
  38.     //修改参数node的next域
  39.      private static final boolean compareAndSetNext(Node node, Node expect, Node update) {
  40.         return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
  41.     }
复制代码
doAcquireSharedInterruptibly()总结
4.5.13 setHeadAndPropagate()方法

setHeadAndPropagate在获取共享资源的时候被调用
  1.     // 唤醒node节点的后继
  2.     private void unparkSuccessor(Node node) {
  3.       
  4.         // 获取node节点的等待状态
  5.         int ws = node.waitStatus;
  6.         if (ws < 0) // 状态值小于0,为SIGNAL -1 或 CONDITION -2 或 PROPAGATE -3
  7.             // cas节点状态为0
  8.             compareAndSetWaitStatus(node, ws, 0);//如果head没有后继的情况下,状态会一直=0
  9.         
  10.         Node s = node.next;
  11.         //若后继为空,或后继已取消,则从尾部往前遍历 找到最靠近的一个处于正常阻塞状态的节点进行唤醒
  12.         // 什么时候s==null ?   node的后继节点是取消状态时,node.next为null
  13.         if (s == null || s.waitStatus > 0) {
  14.             s = null;
  15.             // 由尾节点向前倒着遍历队列,但不会超过node节点
  16.             for (Node t = tail; t != null && t != node; t = t.prev)
  17.                 if (t.waitStatus <= 0)
  18.                     s = t;
  19.         }
  20.         if (s != null)
  21.             LockSupport.unpark(s.thread);//唤醒s节点线程
  22.     }
复制代码
满足调用doReleaseShared的条件分析

setHeadAndPropagate()总结:
4.5.14 doReleaseShared()方法
  1. public final boolean release(int arg) {
  2.         if (tryRelease(arg)) { //如果释放锁成功
  3.             Node h = head;
  4.             // 线程A调用acquire()获取到锁之后,A线程节点变为head,然后A调用release 释放锁,存在两种情况:
  5.             // 1、 如果有新的线程B入队,B成为后继节点,B会将A状态改为SIGNAL,那么(h != null && h.waitStatus != 0 )成立,unparkSuccessor()唤醒后继节点
  6.             // 2、如果A后面没有节点,A状态是默认值:0 ,那么h.waitStatus != 0 不成立,直接返回true,不需要唤醒后继节点。
  7.             if (h != null && h.waitStatus != 0) // 头节点不为空并且头节点状态不为0
  8.                 unparkSuccessor(h); //由head唤醒后继节点
  9.             return true;
  10.         }
  11.         return false;
  12.     }
复制代码
doReleaseShared()总结
4.5.15 releaseShared()方法
  1.    //获取共享资源,响应中断
  2.    public final void acquireSharedInterruptibly(int arg)
  3.             throws InterruptedException {
  4.         if (Thread.interrupted()) //读取线程中断标记,然后擦除标记
  5.             throw new InterruptedException(); //中断标记为true,抛出中断异常,停止执行
  6.         if (tryAcquireShared(arg) < 0)  //调用子类实现方法 获取资源
  7.             doAcquireSharedInterruptibly(arg); //没有获取到,那么再尝试获取(进入队列排队等待)
  8.     }
复制代码
releaseShared()方法总结
5 取消节点移出链表分析

有两种情景,会将取消节点彻底移出链表:
以第一个情景为例子分析:

6 在shared模式中为什么需要PROPAGATE状态

结论:在前驱节点获取资源时,后继也能够有机会申请资源,不需要等待前驱通过releaseShare()来唤醒
分析如下:
  1. //获取共享资源,响应中断
  2. private void doAcquireSharedInterruptibly(int arg)
  3.         throws InterruptedException {
  4.         final Node node = addWaiter(Node.SHARED); //增加等待节点
  5.         boolean failed = true;
  6.         try {
  7.             for (;;) {//无限循环,直到r>0
  8.                 final Node p = node.predecessor(); // p表示 刚插入节点的前驱
  9.                //1、如果前驱是head
  10.                if (p == head) {
  11.                     int r = tryAcquireShared(arg);//调用子类实现方法 尝试获取共享资源
  12.                     if (r >= 0) { // >0 表示 获取到资源
  13.                     // 1、如果是ReentrantReadWriteLock、CountDownLatch ,有可能r=1
  14.                     // 2、如果是Semaphore,有可能r=0
  15.                     // 1、2 都调用setHeadAndPropagate进行共享传播判断
  16.                         setHeadAndPropagate(node, r);// 更新head并进行共享传播
  17.                         p.next = null; // 将队列头节点的next域置空,之后,这个节点将被GC回收
  18.                         failed = false;
  19.                         return;
  20.                     }
  21.                 }
  22.                 // 2、前驱不是head
  23.                 //线程park阻塞,直至被unPark唤醒,或者被其它线程中断唤醒
  24.                 if (shouldParkAfterFailedAcquire(p, node) &&
  25.                     parkAndCheckInterrupt())
  26.                     throw new InterruptedException(); //进入这里表示线程中断标记为true,那么抛出中断异常
  27.             }
  28.         } finally {
  29.             if (failed) //当try 代码块有异常:中断异常 或 其他未知异常,failed才是true
  30.                 cancelAcquire(node);//取消获取资源
  31.         }
  32.     }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4