AQS 源码分析

什么是 AQS

 
AQS,全称是 AbstractQueuedSynchronizer,是位于 java 并发包中的一个抽象类,本文使用的版本是 1.8
 
该类的作用,用开头的一段注释就可以很好的表达
 
/**
 * 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
 
翻译过来就是:此类提供了实现阻塞锁和同步的框架,基于 FIFO 队列来实现。此类被设计为是一个基础的同步器,通过一个原子性的 int 值来表示同步器的状态
 
AQS 实现了两种模式,独占模式和共享模式,分别用于实现独占锁和共享锁
 
以独占模式为例,队列中头节点表示已经获取到了锁,其他节点都处于等待状态。释放锁之后,头节点的下一个节点会尝试去获取锁,但并不意味着一定会成功,如果失败将会继续等待
 

内部类 Node

 
AQS 有两个内部类:Node 和 ConditionObject,这里只介绍 Node 类
 
Node 类是等待队列中的节点类,是一个基于 FIFO 的双向队列
 
主要的属性及方法如下
 
/**
 *   SIGNAL:     当前节点的线程完成或者取消后, 需要设置为此状态以便唤醒后节点的线程
 *   CANCELLED:  节点会被取消如果获取锁超时或者线程被打断, 设置为此状态的节点不会再改变其状态
 *   CONDITION:  表示节点处于 Condtion 队列中, 不会被用作同步队列节点
 *   PROPAGATE:  用于共享模式下的传播释放等待线程
 *   0:          初始值
 *
 * 通过 CAS 来改变属性值
 */
volatile int waitStatus;

/**
 * 指向前节点
 * 当前节点被取消时, 需要指向新的未被取消的节点作为前节点,
 * 头节点不会被取消:因为节点成为头节点就意味着成功获取到了锁, 一个被取消的节点永远不会获取到锁
 */
volatile Node prev;

/**
 * 指向后节点, 当后节点被取消时需要指向新的节点或者空
 */
volatile Node next;

/**
 * 入队节点的线程, 结束之后会被置空
 */
volatile Thread thread;

/**
 *
 * Condition 队列中的下一个等待节点
 */
Node nextWaiter;
 
 

主要属性

 
AQS 主要属性说明如下
 
/**
 * 等待队列的头节点, 赖加载
 * 除了初始化之外, 只能通过 setHead 方法来改变其值
 * 如果 head 不为 null, waitStatus 值就一定不会是 CANCELLED
 */
private transient volatile Node head;

/**
 * 等待队列的尾结点, 懒加载
 * 只能通过 enq 方法添加新节点时才会去改变尾结点
 */
private transient volatile Node tail;

/**
 * 同步器的状态
 * 以 ReentrantLock 为例, 0 表示可以获取到锁, 其他的正整数表示无法获取到锁
 */
private volatile int state;
 
 

方法

 

这里主要分析获取和释放锁相关的代码

 

获取锁

 

1)acquire

 
获取独占锁,方法的逻辑是先尝试获取锁,如果失败,就往队列末尾添加一个节点,再尝试从队列里获取锁
 
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
 

2)tryAcquire

 
尝试获取独占锁,需要由子类自己实现,体现了 Abstract
 
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
 

3)addWaiter

 
往尾部添加一个节点作为新的尾结点。如果尾结点未初始化,则通过 enq 完成初始化
 
private Node addWaiter(Node mode) {
    // 创建当前线程的节点
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        // 通过 cas 将 tail 指向新的节点
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 完成尾结点初始化和设置的工作
    enq(node);
    return node;
}
 

4)acquireQueued

 
不断尝试从队列中获取锁,当成功获取到锁或者线程被打断时会成功退出循环,竞争锁失败的线程会等待直到被唤醒,唤醒之后会再次进入循环尝试去获取锁,不断的重复整个过程
 
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;
            }
            // 获取锁失败后进到这里
            // 先判断线程是否需要 park, 如果是就执行 park, 否则再进入循环
            // 这里还判断了线程是否已经被打断了, 如果是, 就会执行 cancelAcquire
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
 

5)acquireShared

 
获取共享锁,方法逻辑是先尝试获取锁,如果失败,就不断尝试去获取锁
 
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
 

6)tryAcquireShared

 
尝试获取共享锁,需要由子类自己实现
 
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
 

7)doAcquireShared

 
不断尝试从队列中获取共享锁,当成功获取到锁或者线程被打断时会成功退出循环,竞争锁失败的线程会被 park 直到被唤醒,唤醒之后会再次进入循环尝试去获取锁,不断的重复整个过程
 
private void doAcquireShared(int arg) {
    // 在队列末尾加入节点, 模式为 SHARED
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 不断尝试获取锁
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                // 如果前节点是头节点, 就尝试去获取锁
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    // 如果获取锁成果, 就将当前节点设置为新的头节点
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            // 判断是否需要 park 以及线程是否被打断
            // 同 acquireQueued 方法
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
 

释放锁

 

1)release

 
释放独占锁,方法逻辑是首先尝试释放锁,成功后,如果头节点不为空而且 waitStatus 不为 0(即节点不是初始化的状态),就唤醒后节点的线程
 
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
 

2)tryRelease

 
尝试释放独占锁,需要由子类实现
 
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
 

3)releaseShared

 
是否共享锁,逻辑是先尝试释放锁,如果成功,再依次去释放节点上阻塞的线程
 
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
 

4)tryReleaseShared

 
尝试释放共享锁,需要由子类实现
 
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}
 

5)doReleaseShared

 
释放共享锁
 
private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 如果 waitStatus 为 SIGNAL, 表示可以去释放锁
            if (ws == Node.SIGNAL) {
                // 通过 cas 将 waitStatus 设为 0
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // 唤醒当前节点后节点的线程
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}
 
 

共占锁和共享锁的异同

 

相同点

 
1)获取锁前都会判断是否有权限,只有满足条件才可能获取到锁
2)未获取到锁的线程会创建新节点放入队列尾部
 

不同点

 
1)独占锁只会释放头部后节点的线程,而共享锁会依次释放所有线程
2)独占锁存在非公平锁的情况,新的线程可能抢占队列中线程的锁,共享锁则不存在这种情况
 
 

子类实现

 

ReetrankLock

 
ReetrankLock 中的 Sync 内部类实现了 AQS,是一种独占锁,下面来看下它对 AQS 的实现(以 NonfairSync 为例)
 

1)tryAcquire

 
对 AQS 中 tryAcquire 的实现
 
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // state == 0 表示此线程有权限去获取锁
    if (c == 0) {
        // 通过 CAS 保证只有一个线程可以顺利获取到锁
        if (compareAndSetState(0, acquires)) {
            // 设置 exclusiveOwnerThread 为当前线程, 用于实现可重入
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 这里表示获取锁的是当前线程, 提供可重入功能
    else if (current == getExclusiveOwnerThread()) {
        // 用于 state 计数, 每次 unlock 之后都会减 1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
 
 

2)tryRelease

 
对 AQS 中 tryRelease 的实现
 
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        // state == 0 表示锁完全释放, 此时别的线程可以去争夺锁
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
 

CountDownLatch

 
CountDownLatch 中的 Sync 内部类实现了 AQS,是一种共享锁,下面来看下它对 AQS 的实现
 

1)tryAcquireShared

 
state 为 0 时表示可以获取到锁
 
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
 

2)tryReleaseShared

 
state 最终为 0 表示释放锁成功
 
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    for (;;) {
        int c = getState();
        // 此时不需要去释放锁
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
 

posted @ 2022-01-27 15:48  happyhbao  阅读(235)  评论(0)    收藏  举报