AQS

java.util.concurrent.locks.AbstractQueuedSynchronizer,译为抽象队列式同步器
AQS 提供了原子式管理同步状态、阻塞和唤醒线程功能以及等待队列模型的简单框架;

AQS 包含了一个虚拟的Node双向链表(即等待队列),由 volatile 修饰的头&尾节点,以及同步状态标志state,节点等待状态标志waitStatus,和当前线程信息

image.png

  • AQS 提供了共享锁与独占锁的支持

独占锁

同一个时刻只能被一个线程占有,如ReentrantLock,ReentrantWriteLock等,其中又包含了
公平锁与非公平锁

共享锁

同一时间点可以被多个线程同时占有,如ReentrantReadLock,Semaphore等

公平锁 & 非公平锁

初始化

    // 公平锁
    ReentrantLock lock = new ReentrantLock(true);
    // 非公平锁
    ReentrantLock lock = new ReentrantLock();
    ReentrantLock lock = new ReentrantLock(false);

加锁过程

java.util.concurrent.locks.ReentrantLock.FairSync#lock

    final void lock() {
        // 尝试将当前锁的状态置为1 即为加锁成功
        acquire(1);
    }

    public final void acquire(int arg) {
    /**
     * 尝试加锁 tryAcquire = true 则加锁成功
     * 加锁失败时进入后续的等待流程 acquireQueued
     * acquireQueued 将无限循环获取锁
     * 若获取到锁直接开始执行 
     * 或前节点成为头部 自己即将执行 则暂时挂起
     */ 
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        /**
         * 进入此分支时 
         * 当前线程已获取到了锁 
         * 并准备执行 
         * 但由于其他线程打断了此线程 故在此处响应打断
         */
        selfInterrupt();
    }
1.尝试加锁(公平锁模式)
    /**
     * 尝试获取锁 设置锁状态 锁状态的数值实际为获取锁线程的加锁的次数(可重入)
     * 返回true为加锁成功 false为失败
     */
    protected final boolean tryAcquire(int acquires) {
        // 当前线程
        final Thread current = Thread.currentThread();
        // 获取当前锁的状态
        int c = getState();
        // 当前锁状态为0 则为可以加锁
        if (c == 0) {
            /**
             * 非公平锁模式将不会判断队列是否有其他节点 会直接尝试获取锁
             * hasQueuedPredecessors = false 
             * 则等待队列中没有其他线程 
             * 即当前线程是下一个应当获取锁的线程
             */ 
            if (!hasQueuedPredecessors() && 
                /**
                 * 考虑到有其他线程同时判断完毕
                 * 故进行CAS操作 尝试修改锁状态
                 */ 
                compareAndSetState(0, acquires)) {
                // 前两步都已成功 则将当前线程设置为持有锁的线程
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 当前锁状态不为0 且持有锁的线程是当前线程 则直接重入
        else if (current == getExclusiveOwnerThread()) {
            // 设置当前锁状态+1 即当前线程总共上锁的次数
            int nextc = c + acquires;
            // 若上锁次数为负 实为上锁次数越界
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
判断是否等待队列中是否有其他线程(非公平锁没有此步骤)
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    /** 
     * 队列头部 与 队列尾部元素 不相等 
     * 且头部后一个元素不为空 或头部后一个等待线程不是当前线程
     * 说明队列中至少有其他的线程在等待
     */
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
CAS操作进行锁状态变更
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2.加锁失败 进入等待队列

/**
 * 将当前线程构建为等待队列的元素 即Node 
 * 下文将用Node表示当前线程构筑的等待队列元素
 * 将该Node等待的线程设置为当前锁的持有线程
 * CAS操作 尝试将当前Node 设置为队列尾部元素
 */ 
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 的前节点元素
        node.prev = pred;
        // 尝试将当前线程Node设置成尾部元素
        if (compareAndSetTail(pred, node)) {
            // 成功后 修改旧的队尾元素的后一个节点为当前Node
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}    

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 当前Node会在此处无限循环 直到前节点成为头部 自己获取到锁才返回
        for (;;) {
            // 获取当前Node的前节点
            final Node p = node.predecessor();
            // 若当前节点的前节点已成为队列头部 则再次尝试加锁
            if (p == head && tryAcquire(arg)) {
                // 成功后 设置当前Node为头部元素
                setHead(node);
                p.next = null; // help GC
                // 此时已获取到锁 故无需再设置自己为中断状态 
                // 进入队列结果则为失败
                failed = false;
                return interrupted;
            }
            /**
             * shouldParkAfterFailedAcquire 判断当前线程是否能够挂起
             * 清除队列中已取消的节点
             * 判断为true 则可以挂起
             */ 
            if (shouldParkAfterFailedAcquire(p, node) &&
                /**
                 * parkAndCheckInterrupt 执行挂起
                 * 并返回当前线程是否有过中断请求
                 */
                parkAndCheckInterrupt())
                /**
                 * 当前线程在等待过程中无法响应中断 直到获取到锁
                 * 如果在整个等待过程中被中断过 则interrupted = true
                 * acquireQueued最终返回true 否则返回false 
                 * 并在外层响应中断
                 * 需要其他线程调用当前线程的interrupt()方法
                 */
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

解锁过程

public final boolean release(int arg) {
    // 尝试解锁
    if (tryRelease(arg)) {
        // 解锁成功
        Node h = head;
        // 获取当前头部节点 若头部不为空 等待状态不为0 则进行唤醒
        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;
        if (c == 0) {
            // 锁状态为0 则当前线程不再持有该锁 锁变成可上锁状态
            free = true;
            // 设置锁的持有者为空
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
唤醒头节点
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    // 获取节点状态
    int ws = node.waitStatus;
    if (ws < 0)
        // CAS操作设置节点状态为0 即初始化
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    // 获取头节点的下一个节点
    Node s = node.next;
    // 若该节点为空 或状态大于0 则表示该节点已取消
    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);
}

参考资料:
https://blog.csdn.net/Java_zh...
https://blog.csdn.net/Leon_Ji...
https://blog.csdn.net/yy_dieg...


老污的猫
30 声望5 粉丝