Java AQS 初步探索

Java AQS 可以简单理解为是 AbstractQueuedSynchronizer 抽象类。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable

该类是一个抽象类,先查看其父类 AbstractOwnableSynchronizer 源码可知,其主要作用是维护当前占有锁的线程对象。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private static final long serialVersionUID = 3737899427754241961L;

    protected AbstractOwnableSynchronizer() { }

    private transient Thread exclusiveOwnerThread;

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

在介绍 AQS 之前,需要先了解一个类 LockSupport

LockSupport

Basic thread blocking primitives for creating locks and other synchronization classes.

用于创建其它锁和同步类的一个最基本的线程阻塞单元

该类主要由 park() 方法和 unpark(Thread) 及各种变体方法组成,主要是调用 UnSafe 类中的 native 方法实现

private static final sun.misc.Unsafe UNSAFE;

public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

public static void park() {
    UNSAFE.park(false, 0L);
}

相较于wait()notify() | notifyAll() 方法,park()unpark() 的有点

  • 可以指定唤醒的线程(unpark 方法传递的线程对象参数)
  • 不需要与 synchronized 关键字绑定
  • 由于许可的存在,使得可以先调用 unpark() 再调用 park() 也不会导致死锁

park()unpark() 方法主要通过一个许可(permit)来实现关联。

park 方法

调用 park 方法时,都会尝试去获取许可。如果成功获取,就消费掉该许可;如果未能获取到,就阻塞当前线程。

park 方法被阻塞时退出的情况:

  • 线程被中断了,则当该方法返回
  • 其它地方调用当前线程的 unpark 方法
  • The call spuriously (that is, for no reason) returns. 该方法会莫名其妙的返回。

并且该返回并不会返回任何内容和抛出异常,因此在使用 park() 方法的时候,最好在该方法返回后重新检查阻塞线程的条件。

unpark 方法

当调用 unpark 方法时,会给当前线程发放一个许可,该许可始终只有一个,重复调用 unpark 方法并不会导致该许可叠加。

如果在 park 方法之前先调用了 unpark 方法,那么该线程就已经存在了一个许可,后边再调用 park 方法时就不会因此阻塞而导致死锁。

AQS 的主要内容

Node 内部类

Node 是一个等待队列节点类,是锁队列 CLH 的一个变种实现。

static final class Node {
    /** 共享模式 */
    static final Node SHARED = new Node();
    /** 独占模式 */
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled */
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;

    volatile int waitStatus;

    volatile Node prev;

    volatile Node next;

    volatile Thread thread;

    Node nextWaiter;

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


    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;
    }
}

Node是一个双向链表,每个Node中有一个用于跟踪线程是否需要被阻塞的状态字段(waitState),和存储线程信息的 thread 字段,

  • waitState 该字段取值为 -1 ~ 3 之间的整数值:
    • CANCELLED = 1:标记节点状态为已取消。取消的节点其状态不会再改变,并且其线程也不会再被阻塞。
    • SIGNAL = -1:标记当前节点在结束的时候需要唤醒它的下一个节点(next
    • CONDITION = -2:标记当前节点处于条件队列(condition queue)中等待,处于条件队列中时使用该状态。
    • PROPAGATE = -3:标记下一个获取对应应该无条件传播,处于共享状态时使用该状态。
    • 0:普通同步节点初始化状态,默认为 0。
  • prev:指向前驱节点。当前驱节点状态为 CANCELLED 时,会遍历获取另一个 NON-CANCELLED 状态的节点作为新的前驱节点
  • next:指向后继节点。在节点执行入队操作时,不会马上分配一个 next 直到执行了 attachment 操作后。如果 next 节点为CANCELLED 时,则会将next指向自己而不是null
  • thread:存储线程信息。
  • nextWaiter:如果节点处于条件等待队列(CONDITION),则连接到下一个条件等待队列的节点,因此条件队列只是一个单向链表;如果是同步队列,则其值为SHARED

headtail 字段

private transient volatile Node head;

private transient volatile Node tail;

headtail 字段是两个 Node 对象,分别代表了队列的头部和尾部。它们默认都是 null,并且是懒加载,只有在真正用到的时候才会创建实例(即第一次有节点入队的时候,才会创建 headtail)。

head节点:

  • 在初始化的时候,该节点不维护线程;
  • 直到队列中第一次有节点获取到锁,则将获取到锁的节点设置为新的head,新的head前驱节点为null,
  • 每次调用 release 方法时,都会选择 head 节点,并寻找其下一个节点唤醒

tail 节点:

  • 在初始化的时候,tail 为是入队的节点,并且其前驱节点为 head,后继节点为自己
  • 后续每次有一个节点入队的时候,tail 都会重置最新的节点

State 字段

private volatile int state;

protected final int getState() {
    return state;
}

protected final void setState(int newState) {
    state = newState;
}

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

statevolatile 修饰,主要用来维护当前同步器的状态,即是否被某个线程占有。

  • state 的值为 0 的时候,则表示当前同步器没有被任何线程占有,可以获取
  • state 的值不为 0 的时候,则表示已经被其它其它线程占有

下面通过查看 ReentrantLock 中的代码来简单了解 StateNode 的使用

ReentrantLock

该类中主要有三个内部类

  • Sync
  • NonfairSync
  • FairSync

其中 Sync 类继承了 AbstractQueuedSynchronizer 类,而 NonfairSyncFairSync 又都继承了 Sync 类。

ReentrantLock 类中有一个 Sync 的对象,该对象实例化在其构造方法中实现,将其根据参数不同分别实例化为 NonfairSync 对象或者 FairSync 对象。

/**
 * 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 { }
/**
 * Sync object for non-fair locks
 */  
static final class NonfairSync extends Sync { }
/**
 * Sync object for fair locks
 */
static final class FairSync extends Sync { }

/** Synchronizer providing all implementation mechanics */
private final Sync sync;
    
public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

接下来以 ReentrantLock.lock() 方法作为入口,查看具体实现。

lock() 方法

lock 方法如果获取到锁了,那么会立即返回,并且修改 state = 1;如果该锁已经被当前线程获取了,那么修改 state += 1,并且也会立即返回;如果锁被其它线程获取到了,那么就会休眠,直到获取到锁。

public void lock() {
    // ReentrantLock类的lock方法直接调用了 sync 类的实现
    sync.lock();
}

// NonfailSync 类的 lock 方法
final void lock() {
    /*
     compareAndSetState 前边提到了,是修改 AQS 类中 state 的方法
     默认预期当前state值为0(即表示当前锁未被其它线程持有),并欲将其修改为1。
     如果修改成功,则当前线程获取到锁,并且记录当前线程;
     如果修改失败,则表明有其它线程获取到锁了,当前线程开始等待
    */
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
    // 未获取到锁执行 acquire() 方法,该方法是 AQS 类中的方法
        acquire(1);
}

FairSyncNonfairSync 的实现稍有不同:

  • 非公平锁会优先尝试获取锁,如果可以获取到,则直接返回
  • 公平锁则不会有默认尝试
// NonfairSync 类的 lock 实现
final void lock() {
    /*
     compareAndSetState 前边提到了,是修改 AQS 类中 state 的方法
     默认预期当前state值为0(即表示当前锁未被其它线程持有),并欲将其修改为1。
     如果修改成功,则当前线程获取到锁,并且记录当前线程
    */
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
    // 未获取到锁执行 acquire() 方法,该方法是 AQS 类中的方法
        acquire(1);
}

// FairSync 的 lock 方法
final void lock() {
    acquire(1);
}

acquire()AQS 中的 final 方法,但其中的 tryAcquire() 是由子类实现的

public final void acquire(int arg) {
    // tryAcquire() 方法由子类实现
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // acquireQueued 不会响应中断,但在返回后会表明阻塞中是否有被中断过
        selfInterrupt();
}

static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

NonfairSyncFairSync 对于 tryAcquire 的实现也不相同,不过都体现了 ReentrantLock 的可重入锁特性。

对于非公平锁,在 tryAcquire 方法中仍然会优先尝试获取锁;

而公平锁则会先查询是否有其它线程在当前线程之前等待锁,即在队列中是否有排在当前节点之前的节点

// NonfairSync 的 tryAcquire() 实际是调用了 Sync 的 nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    	// 在state = 0时,非公平锁会优先尝试获取锁,如果这里能获取到锁也则会直接返回
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 这里则判断是否为当前线程再次获取锁,如果是,则 state + 1。
    // 这表明同一个线程是可以多次获取锁的,因此 ReentrantLock 又被称为可重入锁
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // 没有获取到锁则返回 false
    return false;
}

// FairSync 对于 tryAcquire 的实现
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 在 state = 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;
}

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());
}

这里假定当前仍然没有获取到锁,那么 tryAcquire 返回 false,则接下来继续查看acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

首先查看addWaiter(Node.EXCLUSIVE),该方法主要用于生成节点并入队

// 这里的参数为 Node.EXCLUSIVE,节点为独占模式
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // 这里会尝试将当前节点设置为tail节点
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 如果快速入队失败,则会调用 enq 方法将节点插入队列
    enq(node);
    return node;
}


private Node enq(final Node node) {
    // 每次都使用AQS插入节点,失败就会一直重试,直到成功为止
    for (;;) {
        Node t = tail;
        // 初始化队列,默认head节点与tail节点指向同一地址
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
        	// 将当前节点设置为tail节点,前驱为上一任tail,后继为自身
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

将节点成功入队之后,则会调用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法。

该方法则会在节点符合条件时,调用 park() 方法阻塞线程

// 对于已经入队的线程,以独占和不可中断的模式获取获取锁
// 该方法在等待过程中不会响应中断,但在返回的时候会返回过程是是否被中断过
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 如果前驱节点是 head 节点,那么就再次调用 tryAcquire 方法尝试获取锁			
            // 如果获取锁成功,则将当前节点设置为新的 head,并直接返回
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 先判断是否符合阻塞的条件,符合则调用 park() 方法进行阻塞
            // 并且在 park() 方法退出后,判断当前线程是否被中断了
            // 即使当前线程被中断了,在没有获取到锁之前,也是不会响应中断的
            // park() 方法会在线程被中断时会返回
            // 这里线程被中断只是将标记设置为 true,还需等待成功获取锁之后再返回
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

// 当节点获取锁失败时,判断节点是否符合被阻塞的条件并更新状态
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    // 如果前驱节点的状态为 SIGNAL,则当前节点可以阻塞
    if (ws == Node.SIGNAL)
        return true;
    // 如果前驱节点状态 > 0,实际为 CANCELLED 状态
    // 则此时就会往前遍历,直到找到一个 NON-CANCELLED 状态的节点,并设置为当前节点的前驱节点
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 尝试修改前驱节点状态为 SIGNAL。当前 waitStatu 必须为 0 | PROPAGATE
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

// 如果检查符合阻塞条件,则调用 park 进行阻塞
// this:这里代表的是 ReentrantLock 中的 Sync sync = new NonfairSync();
// 返回当前线程是否被中断。线程被中断也会导致park()方法返回
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

对于正常 lock 方法执行的流程,不会进入 cancelAcquire方法,后边涉及到了再查看。

ReentrantLock类中的lock()方法到这里就基本上结束了。简单总结一下:

  • state 字段控制整个同步器的状态,即是否被线程占用
  • lock的主要逻辑在 acquire 方法中,其中具体获取锁的方法 tryAcquireAQS 的子类实现。
  • Node中的 waitState 状态实际上是由其后继节点将状态修改为 SIGNAL 状态的,主要用于标记当前节点结束的时候需要去唤醒后继节点
  • 调用 lock 时,如果直接获取到锁,那么此时不会初始化队列,只有在某个线程没有获取到锁,将其入队时,才会初始化队列,即在此时初始化 headtail
  • 在队列中的某个节点如果获取到了锁,那么该节点则会被设置为 head,并设置 node.thread = nullnode.prev = null,只保留其后继节点的引用
  • 如果节点的前驱节点被取消了,那么会一直向前遍历,直到查找到一个不是取消状态的节点,并绑定为新的前驱节点,极端的情况则是会一直遍历到 head 节点。head为当前获取到锁的节点,所以不可能为 CANCELLED状态
  • 在整个由 lock 进入的流程中,正常执行的情况下,节点 waitState 状态只有SINGAL0 两种状态;0:节点初始化时的默认状态,SINGAL:节点在被后插入的节点作为前驱节点引用时,会将节点的状态改为 SINGAL

unlock() 方法

unlock 方法,在当前线程占有锁的时候,会修改 state -= 1;如果 state = 0 则直接释放;如果当前线程没有获取到锁,则会抛出 IllegalMonitorStateException 异常

public void unlock() {
    // 这里实际上是直接调用的 AQS 中的 release() 方法
	sync.release(1);
}

release() 方法是 AQS 中的一个 final 方法,用于释放独占模式的锁。

public final boolean release(int arg) {
    // tryRelease 方法在 AQS 中没有实现,这里仍然是由 Sync 做的具体实现
    // tryRelease在成功释放锁,state = 0 时,返回 true。此时同步器未被任何线程占有
    if (tryRelease(arg)) {
        // 会拿到head节点,并唤醒head节点的后继节点
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 该方法主要是用于唤醒其后继节点,如果后继节点为null,或者 CANCELLED,则会寻找到最近的一个 NON-CALCELLED 节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}

// 唤醒节点的后继节点
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;
    // 如果当前节点的后继节点为null或者状态为CALCELLED
    // 就从tail节点开始,找到离node节点最近的NON-CALCELLED节点
    // 如果找到的节点不为null,则调用其unpark()方法将其唤醒
    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);
}

tryRelease() 具体实现是在 ReentranLock 的内部类 Sync 做的具体实现。

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 如果当前线程不是占有锁的线程,则会直接抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果当前线程释放后 state = 0,则设置占有线程为 Null,并返回 true
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

其它相关方法

lockInterruptibly() 方法

该方法与 lock 的唯一区别就是该方法会响应中断,其它表现与 lock 方法一致

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

public final void acquireInterruptibly(int arg)
    throws InterruptedException {
    // 首先检测是否被中断
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

doAcquireInterruptibly 方法的实现基本与 acquireQueued 方法一致,只是该方法在检测到中断时,会直接响应中断,并抛出 InterruptedException 异常。

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 在检测到中断的时候会直接响应中断
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            // 线程被中断时, 则会执行该方法
            // 主要用于取消当前线程,设置节点状态为 CALCELLED,并唤醒后继节点
            cancelAcquire(node);
    }
}

// 如果 park() 方法退出,则会检测是否被中断了
// Thread.interrupted() 方法会检测中断并清除中断
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

当线程被中断时,就调用 cancelAcquire 取消当前线程

// 用于取消一个正在获取锁的节点
private void cancelAcquire(Node node) {
    if (node == null)
        return;

    node.thread = null;

    // 根据当前节点向前查找一个 NON-CALCELLED 状态的节点
    // 主要目的是为了将当前节点的 next 连接到新的 pred 节点,维护链表的结构
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    // 这里的 predNext 节点状态一定是 CALCELLED 状态
    Node predNext = pred.next;
	
    // 将当前节点状态修改为 CANCELLED
    node.waitStatus = Node.CANCELLED;

    // 如果该节点是 tail 节点,则将 pred 设置为新的tail,并设置其next指向为null
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        int ws;
        // 如果 pred 不是 head 节点,如果是head节点,则不需要标记状态为 SINGAL,因为HEAD在释放锁的时候会去唤醒后继节点
        // 判断当前状态是否为 SIGNAL 或者能否将状态修改为 SIGNAL,如果无法修改,则也会先去尝试唤醒后继节点
        // 状态成功修改后,就将当前节点的后继节点重新绑定为pred节点的后继节点
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            // 用于唤醒后继节点
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

tryLock() 方法

tryLock 方法实际上就是调用了 sync.nonfairTryAcquire() 方法尝试获取锁,成功获取则返回 true,失败则返回 false,不会阻塞

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

tryLock(long timeout, TimeUnit unit) 方法

如果在给定时间内获取到锁,则会立即返回true,若在给定时间内还没有获取到锁,则会返回false

public boolean tryLock(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
    throws InterruptedException {
    // 首先会判断当前线程是否被中断
    if (Thread.interrupted())
        throw new InterruptedException();
    // tryAcquire() 方法的具体实现前边已经说过了,这里就不再赘述
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

doAcquireNanos(int arg, long nanosTimeout) 方法

如果在指定时间内成功获取到锁,则返回 true,如果在规定时间内没有获取到锁,则返回 false

并且,与 acquireQueue 方法不同的是,该方法会响应中断,在判断当前线程已经被中断时,会调用 cancelAcquire 方法。

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);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            // 尝试获取锁
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            nanosTimeout = deadline - System.nanoTime();
            // 如果超过等待时间,则直接返回false
            if (nanosTimeout <= 0L)
                return false;
            // 当节点获取锁失败时,判断节点是否符合被阻塞的条件并更新状态
            if (shouldParkAfterFailedAcquire(p, node) &&
                // static final long spinForTimeoutThreshold = 1000L;
                // 如果时间 < spinForTimeoutThreshold 的值,则进行自旋而不是park
                // 源码中解释说这样能略微的提高响应性
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            // 该方法会响应中断
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            // 线程被中断时, 则会执行该方法
            // 主要用于取消当前线程,设置节点状态为 CALCELLED,并唤醒后继节点
            cancelAcquire(node);
    }
}

通过对 ReentrantLock 源码的探究,可以了解到 State 字段与 Node 类的使用。

State:主要作用就是维护同步状态

Node:每个节点用于维护一个线程的状态,多个节点组成的双向链表用于维护整个同步器的等待队列。

AQS 中的其它内容

上边分析了 Node 中的 prevnext 用于同步器的双向链表结构,而还有一个属性 nextWaiter 似乎没有用到。

该属性要与 AQS 中另一个内部类 ConditionObject 配合使用,用于维护一个条件队列,该队列只有指向后继节点的 nextWaiter 指针,因此条件队列是一个单向链表

ConditionObject 内部类

该类主要有两个字段 firstWaiterlastWaiter 维护条件队列的头部和尾部。

awaitsingal 方法主要用于线程的等待和唤醒。

接下来就直接从 awaitsingal

await 方法

调用 await 方法前,需要先获取到锁

public final void await() throws InterruptedException {
    // 首先在检测到线程被中断时,抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    // 生成一个新的等待节点,并将节点插入到队列中
    Node node = addConditionWaiter();
    // 在节点添加到等待队列之后,尝试释放锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

addConditionWaiter ,该方法主要是生成一个等待节点,并将节点插入条件队列中。

等待节点:即节点的状态为 CONDITION = -2

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 判断尾部节点状态是否为 CANCELLED
    // 对于等待节点,状态只有 CONDITION = -2 | CANCELLED = 1 两种
    if (t != null && t.waitStatus != Node.CONDITION) {
        // 从等待队列中移除状态为 CANCELLED 的节点
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 新建一个等待节点,状态为 CONDITION = -2
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    // 将该节点插入到队列尾部
    // 如果当前尾部节点为 null,则表明当前队列为空,则将头部节点和尾部节点都指向当前节点
    // 后续插入节点,只需要将原本尾部节点的next指向当前节点,并将当前节点设置为新的尾部节点即可
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

// 该方法会遍历整个队列,并移除所有状态为 CANCELLED 的等待节点
private void unlinkCancelledWaiters() {
    Node t = firstWaiter;
    // 该对象指向上一个 NON-CANCELLED 等待节点
    Node trail = null;
    while (t != null) {
        Node next = t.nextWaiter;
        // 如果当前节点状态为 CANCELLED,那么就会将当前节点从队列中移除
        // 并且如果记录的上一个 NON-CANCELLED 节点为 null,即 trail = null,则设置当前节点的后继节点为头部节点
        // 否则设置 trail 指向当前节点的后继节点
        if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            if (trail == null)
                firstWaiter = next;
            else
                trail.nextWaiter = next;
            if (next == null)
                lastWaiter = trail;
        }
        else
            // 如果当前节点状态不为 CANCELLED,则将 trail 指向该 NON-CANCELLED 节点
            trail = t;
        /// 指针往后移动一位,继续循环
        t = next;
    }
}

fullyRelease,在当前节点添加到条件队列后,会尝试释放锁,

final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

isOnSyncQueue

final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null) // If has successor, it must be on queue
        return true;
    /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         */
    return findNodeFromTail(node);
}

private boolean findNodeFromTail(Node node) {
    Node t = tail;
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}

后续有空还会继续更新,如有问题或不足之处还请指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值