AQS 基础篇

AQS 源码解读之加锁篇
AQS 源码解读之解锁篇

简称:抽象的队列同步器。基于设计模式之模板设计模式。
state 变量 + CLH 变种的双端队列

简介

是用来构建锁或者其它同步器组件的重量级基础框架及整个 JUC 体系的基石,通过内置的 FIFO 队列来完成资源获取线程的排队工作,并通过一个 int 类型变量表示持有锁的状态。

加锁会导致阻塞:有阻塞就需要排队,实现排队必然需要某种形式的的队列来进行管理。

抢到资源的线程直接使用处理业务逻辑,抢不到资源的必然涉及一中排队等候机制。抢占资源失败的线程继续去等待,但等候线程仍然保留获取锁的可能且获取锁流程仍在继续。

如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。 这个机制主要的是 CLH 队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是 AQS 的抽象表现。他将请求共享资源的线程封装成队列的节点(Node),通过 CAS、自旋以及 LockSupport.park() 的方式,维护 state 变量的状态,是并发达到同步的控制效果。

qkxv1P.png

为什么 AQS 是 JUC 中的基石

和 AQS 有关的

  • ReentrantLock

    • 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;
      
  • CountDownLatch

    • public class CountDownLatch {
          /**
           * Synchronization control For CountDownLatch.
           * Uses AQS state to represent count.
           */
          private static final class Sync extends AbstractQueuedSynchronizer {
              private static final long serialVersionUID = 4982264981922014374L;
      
  • ReentrantReadWriteLock

    • public class ReentrantReadWriteLock
              implements ReadWriteLock, java.io.Serializable {
          private static final long serialVersionUID = -6992448646407690164L;
          /** Inner class providing readlock */
          private final ReentrantReadWriteLock.ReadLock readerLock;
          /** Inner class providing writelock */
          private final ReentrantReadWriteLock.WriteLock writerLock;
          /** Performs all synchronization mechanics */
          final Sync sync;
      
          /**
           * Creates a new {@code ReentrantReadWriteLock} with
           * default (nonfair) ordering properties.
           */
          public ReentrantReadWriteLock() {
              this(false);
          }
      
          /**
           * Creates a new {@code ReentrantReadWriteLock} with
           * the given fairness policy.
           *
           * @param fair {@code true} if this lock should use a fair ordering policy
           */
          public ReentrantReadWriteLock(boolean fair) {
              sync = fair ? new FairSync() : new NonfairSync();
              readerLock = new ReadLock(this);
              writerLock = new WriteLock(this);
          }
      
          public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
          public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
      
          /**
           * Synchronization implementation for ReentrantReadWriteLock.
           * Subclassed into fair and nonfair versions.
           */
          abstract static class Sync extends AbstractQueuedSynchronizer {
      
  • Semaphore

    • public class Semaphore implements java.io.Serializable {
          private static final long serialVersionUID = -3222578661600680210L;
          /** All mechanics via AbstractQueuedSynchronizer subclass */
          private final Sync sync;
      
          /**
           * Synchronization implementation for semaphore.  Uses AQS state
           * to represent permits. Subclassed into fair and nonfair
           * versions.
           */
          abstract static class Sync extends AbstractQueuedSynchronizer {
      
  • 。。。。。

锁和同步器的关系

  • 锁,面向锁的使用者

    • 定义了程序员和锁交互的使用层 API,隐藏了细节,开发者只管调用即可;
  • 同步器,面向锁的实现者

    • 比如 Java 并发大神 Doug Lea,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知唤醒机制等。

AQS 内部体系架构

qEGRBV.png

实现原理

AQS 使用一个 volatile 的 int 类型的成员变量来表示同步状态,通过内置的 FIFO 队列来完成资源的排队工作将每条要去抢占资源的线程封装成一个 Node 节点来实现锁的分配,通过 CAS 完成对 State 值的修改。

源码注释

Provides a framework for implementing blocking locks and related
synchronizers (semaphores, events, etc) that rely on
first-in-first-out (FIFO) wait queues.  This class is designed to
be a useful basis for most kinds of synchronizers that rely on a
single atomic {@code int} value to represent state. Subclasses
must define the protected methods that change this state, and which
define what that state means in terms of this object being acquired
or released.  Given these, the other methods in this class carry
out all queuing and blocking mechanics. Subclasses can maintain
other state fields, but only the atomically updated {@code int}
value manipulated using methods {@link #getState}, {@link
#setState} and {@link #compareAndSetState} is tracked with respect
to synchronization.
提供一个框架来实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关的同步器(信号量、事件等)。此类旨在为大多数依赖单个原子 {@code int} 值来表示状态的同步器提供有用的基础。子类必须定义更改此状态的受保护方法,并定义该状态在获取或释放此对象方面的含义。鉴于这些,此类中的其他方法执行所有排队和阻塞机制。子类可以维护其他状态字段,但仅跟踪使用方法 {@link getState}、{@link setState} 和 {@link compareAndSetState} 操作的原子更新的 {@code int} 值以进行同步。

APS 的部分源码

     /**
     * 通过自旋等待
     * state 变量判断是否阻塞
     * 从尾部入队,从头部出队
     */
	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;
        
       	// 等待 condition 唤醒
        static final int CONDITION = -2;
        
        // 共享式同步状态获取将会无条件地传播下去
        static final int PROPAGATE = -3;
        
        // 初始为 0,状态是上面的几种
        volatile int waitStatus;
        
		// 前指针
        volatile Node prev;
        
        // 后指针
        volatile Node next;
        
        // 存储的线程
        volatile Thread thread;
        
        // 指向下一个出于 CONDITION 状态的节点
        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        // 返回前驱节点,没有的话抛出 NPE
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

    /**
     * 同步状态
     * 0 就是没有线程占领,可以操作
     * 大于等于 1,有线程占用,需要进行排队
     */
    private volatile int state;

    /**
     * 设置同步状态的值。此操作具有 volatile 写入的内存语义。
     */
    protected final int getState() {
        return state;
    }

    /**
     * 设置同步状态的值。此操作具有 volatile 写入的内存语义。
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * 如果当前状态值等于预期值,则自动将同步状态设置为给定的更新值。此操作具有 volatile 读写的内存语义。
     */
    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

qEtHjs.png

源码解读

通过解读 ReentrantLock,去了解 AQS 的实现原理以及对应的实现流程。

Lock 接口的实现类,基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的。

ReentrantLock 构造方法

// 无参默认创建非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 传参 ? true 创建公平锁 : false 非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

NonfairSync

 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) // overflow
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

FairSync

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        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");
        setState(nextc);
        return true;
    }
    return false;
}

公平锁就比非公平锁多执行了一个 hasQueuedPredecessors() 方法。

hasQueuedPredecessors() 中判断了是否需要排队,导致公平锁和非公平锁的差异如下:
公平锁:线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入等待队列中。
非公平锁:不管是否有等待队列,如果可以获取锁,则立刻占有锁对象,也就是说队列的第一个排队线程在 unpark(),之后还是需要竞争锁。

posted @ 2022-03-19 22:28  李小龙他哥  阅读(101)  评论(0编辑  收藏  举报