Java AbstractQueueSynchronizer(AQS)源码总结从数据结构层面理解AQS

文章目录

  • AQS简介
    • AQS子类需要实现的方法
  • AQS构成
    • AQS队列节点
    • AQS队列
    • 条件队列
  • 操作
    • 查询操作
      • get操作(获取)
        • getExclusiveQueuedThreads操作
        • getFirstQueuedThread操作
          • fullGetFirstQueuedThread操作
        • getQueuedThreads操作
        • getQueueLength操作
        • getSharedQueuedThreads操作
        • getWaitingThreads操作
        • condition.getWaitingThreads操作
        • getWaitQueueLength操作
          • condition.getWaitingThreads操作
      • has操作(判断有没有)
        • hasQueuedPredecessors操作
        • hasQueuedThreads操作
        • hasWaiters操作
          • condition.hasWaiters操作
    • 插入操作
      • AQS插入操作
      • 条件队列插入操作
    • Acquire操作(获取锁操作)
      • acquire操作(获取独占锁操作)
        • acquireQueued操作
        • shouldParkAfterFailedAcquire操作
        • cancelAcquire操作
        • unparkSuccessor操作
      • acquireInterruptibly操作(获取可中断锁操作)
        • doAcquireInterruptibly操作
      • acquireShared操作(获取共享锁操作)
        • doAcquireShared操作
        • setHeadAndPropagate操作
        • doReleaseShared操作
      • acquireSharedInterruptibly操作(获取共享可中断锁操作)
        • doAcquireSharedInterruptibly操作
      • tryAcquireNanos操作 (获取独占可超时锁操作)
        • doAcquireNanos操作
      • tryAcquireSharedNanos操作 (获取共享可超时锁操作)
        • doAcquireSharedNanos操作
    • Release操作(释放锁操作)
      • release操作
      • releaseShared操作

AQS简介

  1. AQS是 AbstractQueueSynchronizer 虚拟队列同步器,是一个虚拟类,子类只需实现其5个方法中的三个就可以构造一个可获取独占锁或获取共享锁的类,没有获取到锁的线程会进入队列排队
  2. 这个类负责实现排队操作,队列中每个节点内部都有一个阻塞的线程,队头的节点是获取锁的线程,队头节点负责唤醒后续一个节点
  3. 其父类 AbstractOwnableSynchronizer 负责记录和设置当前获取锁的线程
    AbstractOwnableSynchronizer 源码可以看我这篇文章 AbstractOwnableSynchronizer
  4. 光看AQS操作可能有些抽象,也不知道它在干嘛,可以看看其子类的实现如 ReentrantLock
    ReentrantLock 源码可以看我这篇文章 ReentrantLock

AQS子类需要实现的方法

方法名 作用
boolean tryAcquire(int arg) 尝试获取独占锁,没有成功则排队
boolean tryRelease(int arg) 尝试释放独占锁
iint tryAcquireShared(int arg) 尝试获取共享锁,没有成功则排队
boolean tryReleaseShared(int arg) 尝试释放共享锁
boolean isHeldExclusively() 判断当前线程是否独占锁

AQS构成

AQS队列节点

  1. SHARED 将该队列标记为共享模式即多个线程可以获取到锁,EXCLUSIVE 将该队列标记为独占模式即只有单个线程可以获取到锁
  2. CANCELLED、SIGNAL、CONDITION、PROPAGATE都是节点的状态值,它们会把值赋给waitStatus
  3. prev、next说明AQS是由双向链表实现的队列
  4. thread说明每个Node节点对应一个线程,说明有一个线程在里面阻塞并排队
  5. nextWaiter是下一个条件队列节点
  6. NEXT、PREV、THREAD、WAITSTATUS是变量句柄,是JDK9新特性,通过它们可以对应的变量进行CAS操作和原子操作,不需要再像以前把变量定义为原子类变量了,如NEXT可以对next进行CAS和原子性操作,如果想要达到这种效果只有把prev定义为AtomicReference< Node > 类型
static final class Node {
     
   	static final Node SHARED = new Node();
    static final Node EXCLUSIVE = null;
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    volatile int waitStatus;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    
    private static final VarHandle NEXT;
    private static final VarHandle PREV;
    private static final VarHandle THREAD;
    private static final VarHandle WAITSTATUS;
}

AQS队列

  1. head就是队列的头节点,tail就是队列的尾节点,state在其子类实现一般理解为获取锁的次数
  2. STATE、HEAD、TAIL 是对应变量的变量句柄
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
     
    
  	private transient volatile Node head;
  	private transient volatile Node tail;
	private volatile int state;

	private static final VarHandle STATE;
    private static final VarHandle HEAD;
    private static final VarHandle TAIL;
}

条件队列

可以看出条件队列里面也是使用Node节点作为其组成,只是使用的是Node节点的nextWaiter属性,相当于单向链表

public class ConditionObject implements Condition, java.io.Serializable {
     
	 private transient Node firstWaiter;
	 private transient Node lastWaiter;
}

操作

查询操作

get操作(获取)

getExclusiveQueuedThreads操作

作用:获取队列中处于独占锁模式的线程列表
实现:从双向链表尾部开始查找,尾部查找原因在插入enq里面,如果Node节点不是共享节点,则取出节点中存放的线程并将其加入列表

public final Collection<Thread> getExclusiveQueuedThreads() {
     
    ArrayList<Thread> list = new ArrayList<>();
    for (Node p = tail; p != null; p = p.prev) {
     
        if (!p.isShared()) {
     
            Thread t = p.thread;
            if (t != null)
                list.add(t);
        }
    }
    return list;
}

getFirstQueuedThread操作

作用:获取队列中第一个等待的线程
实现:如果头节点等于尾结点说明没有线程排队,否则执行fullGetFirstQueuedThread返回队头后面一个结点内部的线程,或者后续结点内部的线程

public final Thread getFirstQueuedThread() {
     
    // handle only fast path, else relay
    // 仅处理快速路径,否则为转发
    return (head == tail) ? null : fullGetFirstQueuedThread();
}
fullGetFirstQueuedThread操作
  1. 连续检查两次,队头h不为空 ,且队头下一个节点s也不为空,且s的前一个节点为队头h,且s存储的线程也不为空,则返回队头下一个节点的线程st
  2. 为什么不返回队头节点内部的线程,因为队头节点内部的线程一般为null,为null表示已经获取到锁在运行了,不为null说明仍然在排队
  3. 如果以上条件不满足,则从队列尾部开始往前找,找到最靠近队头的一个内部线程不为空的节点,再返回该节点内部的线程
private Thread fullGetFirstQueuedThread() {
     
        Node h, s;
        Thread st;
        if (  //队头不为空  且 队头下一个也不为空 且队头的下一个的前一个为队头 且存储的线程也不为空
            ((h = head) != null && (s = h.next) != null &&
             s.prev == head && (st = s.thread) != null)
                ||
            //队头不为空  且 队头下一个也不为空 且队头的下一个的前一个为队头 且存储的线程也不为空
            ((h = head) != null && (s = h.next) != null &&
             s.prev == head && (st = s.thread) != null)
            )
            return st;
            
        Thread firstThread = null;
        // 则从队列尾部开始往前找,找到最靠近队头的一个内部线程不为空的节点
        for (Node p = tail; p != null && p != head; p = p.prev) {
     
            Thread t = p.thread;
            if (t != null)
                firstThread = t;
        }
        return firstThread;
    }

getQueuedThreads操作

作用:获取队列中等待的线程集合
实现:从双向链表尾部开始查找,如果Node节点内部线程不为空,则取出节点中存放的线程并将其加入列表

 public final Collection<Thread> getQueuedThreads() {
     
    ArrayList<Thread> list = new ArrayList<>();
    for (Node p = tail; p != null; p = p.prev) {
     
        Thread t = p.thread;
        if (t != null)
            list.add(t);
    }
    return list;
}

getQueueLength操作

作用:获取队列中等待的线程数量
实现:从双向链表尾部开始查找,如果Node节点内部线程不为空,则计数器加1

public final int getQueueLength() {
     
    int n = 0;
    for (Node p = tail; p != null; p = p.prev) {
     
        if (p.thread != null)
            ++n;
    }
    return n;
}

getSharedQueuedThreads操作

作用:获取队列中共享(共享锁)结点等待的线程集合
实现:从双向链表尾部开始查找,如果Node节点是共享结点且内部线程不为空,则取出节点中存放的线程并将其加入列表

public final Collection<Thread> getSharedQueuedThreads() {
     
   ArrayList<Thread> list = new ArrayList<>();
    for (Node p = tail; p != null; p = p.prev) {
     
        if (p.isShared()) {
     
            Thread t = p.thread;
            if (t != null)
                list.add(t);
        }
    }
    return list;
}

getWaitingThreads操作

作用:获取条件队列中等待的线程集合
实现:从单向链表中往后查询,找到Node状态为CONDITION且其内部线程不为null的结点,取出内部线程放入集合,同步队列和条件队列都使用Node结点作为其元素

public final Collection<Thread> getWaitingThreads(ConditionObject condition) {
     
   if (!owns(condition))
        throw new IllegalArgumentException("Not owner");
    return condition.getWaitingThreads();
}

condition.getWaitingThreads操作

从Node结点的nextWaiter单向链表实现的条件队列往后找,状态为CONDITION且内部线程Thread不为空的结点,取出其内部线程并放入集合

 protected final Collection<Thread> getWaitingThreads() {
     
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    ArrayList<Thread> list = new ArrayList<>();
    for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
     
        if (w.waitStatus == Node.CONDITION) {
     
            Thread t = w.thread;
            if (t != null)
                list.add(t);
        }
    }
    return list;
}

getWaitQueueLength操作

作用:获取条件队列中等待的线程数量
实现:从单向链表中往后查询,找到Node状态为CONDITION且其内部线程不为null的结点,计数器加1

 public final int getWaitQueueLength(ConditionObject condition) {
     
     if (!owns(condition))
         throw new IllegalArgumentException("Not owner");
     return condition.getWaitQueueLength();
 }
condition.getWaitingThreads操作

从Node结点的nextWaiter单向链表实现的条件队列往后找,如果结点状态为CONDITION则计数器加一

protected final int getWaitQueueLength() {
     
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int n = 0;
    for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
     
        if (w.waitStatus == Node.CONDITION)
            ++n;
    }
    return n;
}

has操作(判断有没有)

hasQueuedPredecessors操作

作用:判断队列中有没有排队的线程
实现:

  1. 取出头节点h,如果不为空,且其后续一个结点s也不为空,则判断s内部线程是不是当前线程,如果不是,说明有线程在排队,返回true
  2. 如果结点s为空,则需要从双向链表尾部往前找,找到最靠近头节点h且不为空且其状态小于等于0的结点设为s,状态小于等于0说明不是CANCELLED取消状态,判断这个新的s的内部线程是否为当前线程如果不是,说明有线程在排队
public final boolean hasQueuedPredecessors() {
     
     Node h, s;
     if ((h = head) != null) {
     
         if ((s = h.next) == null || s.waitStatus > 0) {
     
             s = null; 
             for (Node p = tail; p != h && p != null; p = p.prev) {
     
                 if (p.waitStatus <= 0)
                     s = p;
             }
         }
         if (s != null && s.thread != Thread.currentThread())
             return true;
     }
     return false;
 }

hasQueuedThreads操作

作用:判断队列中有没有状态小于等于0的结点

public final boolean hasQueuedThreads() {
     
    for (Node p = tail, h = head; p != h && p != null; p = p.prev)
        if (p.waitStatus <= 0)
            return true;
    return false;
}

hasWaiters操作

作用:判断队列中有没有排队的线程
实现:必须要获取错才能执行,判断条件队列中有无结点状态为CONDITION

public final boolean hasWaiters(ConditionObject condition) {
     
    if (!owns(condition))
         throw new IllegalArgumentException("Not owner");
     return condition.hasWaiters();
 }
condition.hasWaiters操作
protected final boolean hasWaiters() {
     
   if (!isHeldExclusively())
         throw new IllegalMonitorStateException();
     for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
     
         if (w.waitStatus == Node.CONDITION)
             return true;
     }
     return false;
 }

插入操作

AQS插入操作

  1. 获取尾节点,如果尾节点不为空,将node节点的prev设置为尾节点,这已经把双向链表的前向连接好了
  2. 用CAS将尾节点设为新插入的节点,如果成功则将以前的尾节点的后向连接练好,可以看到前向连接是先连好的,所以这也是AQS队列的几乎所有查询操作都从尾节点开始往前找的原因
  3. 如果尾节点为空,则初始化队列即创建一个节点将其同时设为头节点和尾节点
private Node enq(Node node) {
     
        for (;;) {
     
            // 获取尾节点
            Node oldTail = tail;
            // 如果尾节点不为空
            if (oldTail != null) {
     
                // 将node节点的prev设置为尾节点
                node.setPrevRelaxed(oldTail);
                // CAS改变尾节点,若此时尾节点还是oldTail,则将尾节点设为node
                if (compareAndSetTail(oldTail, node)) {
     
                    // 如果上面操作成功则将以前的尾节点的后一个节点设为新节点
                    // 否则自旋直至设置node为尾节点成功
                    oldTail.next = node;
                    return oldTail;
                }
            } 
            // 如果尾节点为空,说明队列为空
            else {
     
                // 创建一个队列,即将头和尾设置为同一个新创建的节点
                // 然后进入自旋,只有在上面插入成功了才会跳出循环
                initializeSyncQueue();
            }
        }
    }

条件队列插入操作

  1. 可以看出条件队列插入和AQS插入两者差别不大,但有两个差别,如下
  2. Node node = new Node(mode) 其实是创建一个节点,该节点的prev、next全为null,如果是独占模式连nextWaiter属性也为空,如果是共享模式nextWaiter属性设为传入的mode,还有一个操作是把node节点的thread设为当前线程
  3. AQS插入返回的是尾节点,条件队列插入返回的是当前节点
  4. 可以条件队列插入其实会影响AQS队列,相对于也在AQS队列尾部插入一个Node节点,只是该Node节点的nextWaiter属性有效,可以通过这个属性找到条件队列下一个节点
private Node addWaiter(Node mode) {
     
        //Node.EXCLUSIVE 传入null 则创建null 则在下面初始化头尾节点
        //Node.SHARED 传入 new Node() 是nextWaiter为 new Node()
        Node node = new Node(mode);

        for (;;) {
     
        	 // 获取尾节点
            Node oldTail = tail;
            // 如果尾节点不为空
            if (oldTail != null) {
     
                // CAS改变尾节点,若此时尾节点还是oldTail,则将尾节点设为node
                node.setPrevRelaxed(oldTail);
                // 使用CAS设置node为尾节点
                if (compareAndSetTail(oldTail, node)) {
     
                	// 如果上面操作成功则将以前的尾节点的后一个节点设为新节点
                    // 否则自旋直至设置node为尾节点成功
                    oldTail.next = node;
                    return node;
                }
            } else {
     
                // 创建一个队列,即将头和尾设置为同一个新创建的节点
                // 然后进入自旋,只有在上面插入成功了才会跳出循环
                initializeSyncQueue();
            }
        }
    }

Acquire操作(获取锁操作)

acquire操作(获取独占锁操作)

  1. 尝试获取锁,如果没有成功,则先执行addWaiter往条件队列添加节点,但同时影响AQS队列,由于是独占模式nextWaiter属性为null
  2. acquireQueued方法自旋判断传入的Node结点(刚刚使用addWaiter创建并返回的)是否排队到队头,到队头再次尝试获取锁,如果还没成功使用CAS尝试将Node状态设为SIGNAL并当将当前线程阻塞,等待前一个结点的唤醒
  3. arg 一般传入的是获取锁的次数
  4. selfInterrupt 是如果没有获取到锁且在自旋阻塞等待过程中收到中断信号,则进行自我中断
 public final void acquire(int arg) {
     
    // 如果获取锁没有成功 且
    if (!tryAcquire(arg) &&
    	// 先执行addWaiter添加条件队列节点(同时影响AQS队列),独占模式nextWaiter为空
        // 然后自旋方法检测排队是否到队头,到了尝试获取锁,没有则设置状态为SIGNEL,并阻塞当前线程
         // 自旋方法排队是否到队头,到了尝试获取锁,没有则设置状态为SIGNEL
         // 设置成功进行阻塞
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 上面是死循环方法而且嵌套在if语句条件中,如果出循环且返回true说明检测到中断状态需要进行自我中断
        // 进行自我中断
        selfInterrupt();
}

acquireQueued操作

  1. 循环(自旋),获取当前结点前一结点,当前结点已经是经过addWaiter添加到双向链表了所以会有前一结点,如果前一结点是头节点,才尝试获取锁,获取成功将当前结点设为头节点,将当前结点中thread属性置空,说明头节点就是获取到锁的结点,且获取到锁的结点thread属性为空
  2. 没有获取到锁执行shouldParkAfterFailedAcquire方法将当前结点的前一结点p的状态设为SIGNAL,如果p状态大于0,说明前一结点需要释放,则往前找一个状态小于0正常的结点,重新连接前面结点和当前结点,如果前面操作成功阻塞当前线程并返回是否需要中断
  3. 出现任何错误,则移除双向链表中的node结点
final boolean acquireQueued(final Node node, int arg) {
     
     boolean interrupted = false;
      try {
     
          for (;;) {
     
              // 返回node的上一个节点
              // 循环后如果node上一个节点改变,则会抛出异常
              final Node p = node.predecessor();
              // tryAcquire 尝试以独占模式获取
              if (p == head && tryAcquire(arg)) {
     
                  // 设置该节点为头节点
                  setHead(node);
                  // 之前的头节点让GC回收
                  p.next = null; // help GC
                  return interrupted;
              }
              // 独占没有获取成功
              // 则尝试设为p的状态为SIGNAL,如果p为要释放节点,则p将往前移
              if (shouldParkAfterFailedAcquire(p, node))
                  // 位操作 | 相当于 逻辑||
                  // 方便的阻塞方法,然后检查是否中断。
                  interrupted |= parkAndCheckInterrupt();
          }
      } catch (Throwable t) {
     
          // 取消获得
          // 从队列中移除该节点
          cancelAcquire(node);
          // 判断是否要添加中断标记
          if (interrupted)
              selfInterrupt();
          throw t;
      }
  }

shouldParkAfterFailedAcquire操作

  1. 获取前一结点的状态,如果为SIGNAL则返回
  2. 如果状态大于0说明是CANCELLED状态,则前一节点要被取消,则再往前寻找无须被取消的结点,将其与当前结点node相连
  3. 使用CAS将前一结点状态设为SIGNAL,可能设置不成功,不管设置成功与否都会返回false,直到外部下一次自旋时再返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
     
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
     
            do {
     
                // 跳过中间waitStatus大于0的节点
                // pred = pred.prev;
                // node.prev = pred;
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
      
            // 设置状态为SIGNAL
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }

cancelAcquire操作

1.取消当前节点,如果当前结点为空则直接返回
2.如果当前节点存在,则先将其内部线程置空,再往前找状态不大于0的节点存为pred,pred的后一个节点也是要取消的节点存为predNext,设置当前节点状态为CANCELLED
3.使用CAS将尾节点设为前面有效节点pred,成功后再将pred.next置为空
4.CAS操作没有成功则说明此时尾节点已经被替换,说明可能新传入一个节点,此时要判断两个情况,如果pred已经是队头则调用unparkSuccessor唤醒node的后一个节点因为node要被取消了,如果pred不是队头还在排队,则将node下一个节点设为pred的下一个节点,则可以将node从双向链表中移除了

private void cancelAcquire(Node node) {
     
        if (node == null)
            return;
        //内部线程置空
        node.thread = null;
   
   		//前找状态不大于0的节点存为prev
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            // pred = pred.prev;
            // node.prev = pred
            node.prev = pred = pred.prev;

		
        Node predNext = pred.next;
        // 设置当前节点状态为CANCELLED
        node.waitStatus = Node.CANCELLED;

		// 使用CAS将尾节点设为前面有效节点pred,成功后再将pred.next置为空
        if (node == tail && compareAndSetTail(node, pred)) {
     
            pred.compareAndSetNext(predNext, null);
        } 
		// CAS操作没有成功则说明此时尾节点已经被替换,说明可能新传入一个节点
		else {
     
            int ws; 
            if (pred != head &&
                // 先前一个节点状态为SIGNAL 或 
                //状态小于0且先前一个节点状态CAS设为SIGNAL(-1)成功且先前一个节点线程不为空
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
                pred.thread != null) {
     
                // 获取节点后一个节点
                Node next = node.next;
                // 如果节点后一个节点非空,且状态小于等于0,则将pred节点的下一个节点设为next节点
                if (next != null && next.waitStatus <= 0)
                    pred.compareAndSetNext(predNext, next);
            } 
			// 如果pred已经是队头
			else {
     
                unparkSuccessor(node);
            }
            node.next = node; // help GC
        }
    }

unparkSuccessor操作

  1. 获取当前节点状态,如果小于0则是功能节点,将其设为0为普通节点,SIGNAL为-1表示节点负责唤醒后续节点
  2. 判断node后续节点是否有效,即不为空且状态不为CANCELLED
  3. 无效则从尾节点开始找最靠近node的有效节点,并解除其内部线程阻塞
 private void unparkSuccessor(Node node) {
     
 		//获取当前节点状态
        int ws = node.waitStatus;
        // 如果小于0则是功能节点,将其设为0为普通节点
        if (ws < 0)
            node.compareAndSetWaitStatus(ws, 0);
            
        Node s = node.next;
        // 寻找node后续的有效节点,即不为空且状态不为CANCELLED
        if (s == null || s.waitStatus > 0) {
     
            s = null;
            // 无效则从尾节点开始找最靠近node的有效节点
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        if (s != null)
            // 解除其内部线程阻塞
            LockSupport.unpark(s.thread);
    }

acquireInterruptibly操作(获取可中断锁操作)

  1. 先检测线程是否打上了中断标志,打上了则抛出异常
  2. 如果尝试获取锁没有成功,则进行可中断自旋
public final void acquireInterruptibly(int arg)
       throws InterruptedException {
     
     //先检测线程是否打上了中断标志,打上了则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
       //尝试获取锁没有成功,则
    if (!tryAcquire(arg))
        // 自旋 循环尝试获取锁,只不过循环一次会阻塞一次,
        doAcquireInterruptibly(arg);
}

doAcquireInterruptibly操作

和acquireQueued类似

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
     
        final Node node = addWaiter(Node.EXCLUSIVE);
        try {
     
            for (;;) {
     
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
     
                    setHead(node);
                    p.next = null; // help GC
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
     
            cancelAcquire(node);
            throw t;
        }
    }

acquireShared操作(获取共享锁操作)

tryAcquireShared尝试获取共享锁没有成功,则自旋获取

public final void acquireShared(int arg) {
     
	// 尝试获取共享锁没有成功
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

doAcquireShared操作

  1. 和acquireQueued类似,但往双向链表添加的Node节点的nextWaiter属性不再为空
  2. 当前一节点为头节点才尝试获取锁,获取成功则调用setHeadAndPropagate方法,否则阻塞
private void doAcquireShared(int arg) {
     
     final Node node = addWaiter(Node.SHARED);
     boolean interrupted = false;
     try {
     
         for (;;) {
     
             final Node p = node.predecessor();
             // 前一节点为头节点
             if (p == head) {
     
                 int r = tryAcquireShared(arg);
                 if (r >= 0) {
     
                     setHeadAndPropagate(node, r);
                     p.next = null; // help GC
                     return;
                 }
             }
             if (shouldParkAfterFailedAcquire(p, node))
                 interrupted |= parkAndCheckInterrupt();
         }
     } catch (Throwable t) {
     
         cancelAcquire(node);
         throw t;
     } finally {
     
         if (interrupted)
             selfInterrupt();
     }
 }

setHeadAndPropagate操作

设置node为头节点,并调用doReleaseShared方法唤醒node后续节点

private void setHeadAndPropagate(Node node, int propagate) {
     
        Node h = head; 
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
     
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

doReleaseShared操作

  1. 取出头节点,如果头节点状态为SIGNAL,说明用来唤醒后续节点,则先用CAS将头节点状态设为0,再唤醒后续节点,如果CAS设置失败,则进入下一次循环
  2. 如果头节点状态为0,则将其设为PROPAGATE传播状态,如果CAS设置失败,则进入下一次循环
private void doReleaseShared() {
     
    for (;;) {
     
        Node h = head;
        if (h != null && h != tail) {
     
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
     
                if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                    continue;            
                    //唤醒节点的后续节点(如果存在)。
                    unparkSuccessor(h);
            } else if (ws == 0 && 
                     !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                continue;                
        }
        if (h == head)                  
            break;
    }
}

acquireSharedInterruptibly操作(获取共享可中断锁操作)

尝试获取共享锁没有成功则调用doAcquireSharedInterruptibly自旋

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
     
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

doAcquireSharedInterruptibly操作

和doAcquireShared差别不大

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
     
    final Node node = addWaiter(Node.SHARED);
    try {
     
        for (;;) {
     
            final Node p = node.predecessor();
            if (p == head) {
     
                int r = tryAcquireShared(arg);
                if (r >= 0) {
     
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
     
        cancelAcquire(node);
        throw t;
    }
}

tryAcquireNanos操作 (获取独占可超时锁操作)

如果获取独占锁成功则返回,否则自旋获取锁,有超时返回的功能,返回true表示获取成功,返回false表示超时

 public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
     
    if (Thread.interrupted())
         throw new InterruptedException();
       
     // 如果获取锁成功则返回
     return tryAcquire(arg) ||
     	// 否则自旋获取锁,有超时返回的功能
         doAcquireNanos(arg, nanosTimeout);
 }

doAcquireNanos操作

  1. 和acquireQueue操作差不多,只是多了计时操作,如果超时则调用cancelAcquire释放节点
  2. 而且每循环一次也会阻塞,和前面的一直阻塞不同,这是调用LockSupport.parkNanos(this, nanosTimeout),进行定时阻塞,超时则解除阻塞
private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
     
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        
        final Node node = addWaiter(Node.EXCLUSIVE);
        try {
     
            for (;;) {
     
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
     
                    setHead(node);
                    p.next = null; // help GC
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L) {
     
                    cancelAcquire(node);
                    return false;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
     
            cancelAcquire(node);
            throw t;
        }
    }

tryAcquireSharedNanos操作 (获取共享可超时锁操作)

如果获取共享锁成功则返回,否则自旋获取锁,有超时返回的功能,返回true表示获取成功,返回false表示超时

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
     
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

doAcquireSharedNanos操作

  1. 和doAcquireShared操作差不多,只是多了计时操作,如果超时则调用cancelAcquire释放节点
  2. 而且每循环一次也会阻塞,和前面的一直阻塞不同,这是调用LockSupport.parkNanos(this, nanosTimeout),进行定时阻塞,超时则解除阻塞
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
     
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        try {
     
            for (;;) {
     
                final Node p = node.predecessor();
                if (p == head) {
     
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
     
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L) {
     
                    cancelAcquire(node);
                    return false;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
     
            cancelAcquire(node);
            throw t;
        }
    }

Release操作(释放锁操作)

release操作

tryRelease是子类实现的,如果释放独占锁成功,则判断头节点是否为空,不为空则判断头结点状态,如果头结点状态为0,说明已经唤醒过后续结点了,不需要再唤醒,否则则调用unparkSuccessor唤醒后续结点

public final boolean release(int arg) {
     
    if (tryRelease(arg)) {
     
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

releaseShared操作

tryRelease是子类实现的,如果释放共享锁成功,则调用doReleaseShared()唤醒后续结点,doReleaseShared代码上面有介绍

public final boolean releaseShared(int arg) {
     
   if (tryReleaseShared(arg)) {
     
        doReleaseShared();
        return true;
    }
    return false;
}

你可能感兴趣的:(java部分类源码,java,数据结构)