AbstractQueuedSynchronizer源码分析

195 阅读4分钟
  • AbstractQueuedSynchronizer类的常用属性
    // 头结点,可以理解为当前持有锁的线程
    private transient volatile Node head;
    
    // 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表
    private transient volatile Node tail;
    
    // 代表当前锁的状态,0代表没有被占用,大于0代表有线程持有当前锁,这个值可以大于 1,是因为锁可以重入,每次重入都加上1
    private volatile int state;
    
    // 代表当前持有独占锁的线程,锁重入时可以用这个来判断当前线程是否已经拥有了锁
    private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer
    
  • Node类的常用属性
    static final class Node {
        // 标识节点当前在共享模式下
        static final Node SHARED = new Node();
        // 标识节点当前在独占模式下
        static final Node EXCLUSIVE = null;
      
        //下面的几个int常量是给waitStatus用的
        // 代码此线程取消了争抢这个锁
        static final int CANCELLED = 1;
        // 表示当前node的后继节点对应的线程需要被唤醒
        static final int SIGNAL = -1;
        // 线程正在唤醒等待条件
        static final int CONDITION = -2;
        // 线程的共享锁应该被无条件传播
        static final int PROPAGATE = -3;
      
        // 取值为上面的1、-1、-2、-3,或者0,如果这个值大于0代表此线程取消了等待
        volatile int waitStatus;
        // 前驱节点的引用
        volatile Node prev;
        // 后继节点的引用
        volatile Node next;
        // 线程
        volatile Thread thread;
    
    以ReentrantLock为例,ReentrantLock 在内部用了内部类 Sync 来管理锁,所以真正的获取锁和释放锁是由 Sync 的实现类来控制的。Sync 有两个实现,分别为 非公平锁NonfairSync 公平锁公平锁FairSync,我们看 FairSync 部分。
  • 获取锁:通过tryAcquire尝试获取锁,如果获取失败,将当前线程包装成node,通过addWaiter方法加入阻塞队列队尾,acquireQueued执行挂起线程等操作
    public final void acquire(int arg) {
       if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    	   selfInterrupt();
    }
    
     protected final boolean tryAcquire(int acquires) {
         final Thread current = Thread.currentThread();
          int c = getState();
          // state为0表示锁没有被占用
          if (c == 0) {
    	  // 公平锁,先判断队列有没有线程在等待,没有线程等待,使用cas将state设置为1
              if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
                  setExclusiveOwnerThread(current);
                  return true;
              }
          }
          // 没拿到锁,判断占有锁的线程是否是当前线程,如果是当前线程则可重入,
          else if (current == getExclusiveOwnerThread()) {
              int nextc = c + acquires;
              if (nextc < 0)
                  throw new Error("Maximum lock count exceeded");
              // state加1
              setState(nextc);
              return true;
          }
          return false;
      }
    
    acquireQueued方法处理抢占锁失败挂起线程,被唤醒后去获取锁等操作。这里的for循环只有在抢占锁成功后返回,如果线程被挂起,又被前驱结点唤醒,则变成对头结点,那么抢占锁成功,介绍循环。
     final boolean acquireQueued(final Node node, int arg) {
           boolean failed = true;
           try {
               boolean interrupted = false;
               for (;;) {
                   final Node p = node.predecessor();
                   // 是头结点,而且抢占锁成功,返回
                   if (p == head && tryAcquire(arg)) {
                       setHead(node);
                       p.next = null; // help GC
                       failed = false;
                       return interrupted;
                   }
                   // 不是队头结点,或者抢占锁失败,shouldParkAfterFailedAcquire方法返回true,
                   // 执行shouldParkAfterFailedAcquire方法挂起线程,如果被前驱结点释放锁后唤醒,也是从这里开始执行。
                   if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                       interrupted = true;
               }
           } finally {
               if (failed)
                   cancelAcquire(node);
           }
       }
    
    当前线程没有抢到锁,是否需要挂起当前线程?
     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // Node.SIGNAL表示待唤醒,前驱结点状态正常,当前线程需要挂起,直接返回
        if (ws == Node.SIGNAL)         
            return true;
        // 大于0表示取消了等待,修改前驱结点
        if (ws > 0) {        
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {       
    	        // state初始化为0,设置state为-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    挂起线程
     private final boolean parkAndCheckInterrupt() {
           LockSupport.park(this);
           return Thread.interrupted();
       }
    
  • 释放锁
    public final boolean release(int arg) {
       if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    protected final boolean tryRelease(int releases) {
    	 int c = getState() - releases;
    	 if (Thread.currentThread() != getExclusiveOwnerThread())
    	     throw new IllegalMonitorStateException();
    	 boolean free = false;
    	 // c为0表示锁没有重入了,锁可以释放
    	 if (c == 0) {
    	     free = true;
    	     setExclusiveOwnerThread(null);
    	 }
    	 setState(c);
    	 return free;
    }
    
    唤醒后继结点
     private void unparkSuccessor(Node node) {      
        int ws = node.waitStatus;
        //如果head节点当前waitStatus<0, 将其修改为0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
              // 唤醒线程
            LockSupport.unpark(s.thread);
    }
    
    线程被唤醒后,从以下代码中继续往前走,回到acquireQueued方法
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 刚刚线程被挂起在这里了
        return Thread.interrupted();
    }
    
  • 参考 blog.csdn.net/zjwl199802/…