AQS 源码解读系列--ReentrantLock 篇

在 JDK1.6 之前,JDK 中实现同步我们只能使用 synchronized 关键字,尽管从 JDK1.5 开始 Java 官方对重量级的 synchronized 进行了升级,引入了偏向锁及自旋锁,使得语言层面的锁更加的轻量和高效,但是始终不能满足用户对锁灵活控制的需求,所以才有了 ReentrantLock 的出现。

1. lock()

1.1 时序图

AQS 源码解读系列--ReentrantLock 篇_第1张图片

1.2 源码解读

ReentrantLock.lock()

public void lock() {
    sync.lock();
}

NonfairSync.lock()

final void lock() {
	// 非公平锁,上来就去尝试获取锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
    	// 获取失败,进入自旋获取锁的逻辑
        acquire(1);
}

AbstractQueuedSynchronizer.acquire()

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
    	// 再次非公平的尝试获取锁失败,将当前线程构建成 Node 节点,加入等待队列
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

NonfairSync.tryAcquire()

protected final boolean tryAcquire(int acquires) {
	return nonfairTryAcquire(acquires);
}

Sync.nonfairTryAcquire()

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();// state 表示锁被获取的次数
    // 这里再次体现了锁的非公平性,此时不是去乖乖的排队等待,而是再次尝试获取锁
    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");
        // 因为当前线程即为锁的 owner,因此这里不需要 CAS,而是直接更新
        setState(nextc);
        return true;
    }
    return false;
}

AbstractQueuedSynchronizer.addWaiter()

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;
    // 等待队列是一个双向链表
    // 如果尾节点不为空,将当前线程构建成的节点通过 CAS 添加到队列的尾部
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 如果队列为空,或者加入队列的时候失败,进入这里的逻辑
    enq(node);
    return node;
}
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // 尾节点为空,说明此时是空队列,构造一个空节点作为头节点
        // 头节点代表当前获得锁的线程对应的节点
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {// 如果尾不为空,当前线程会自旋入队
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

AbstractQueuedSynchronizer.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;
            }
            // 如果前驱节点不是头节点,或者上面的获取锁操作失败,会走到这里
            // 通过 shouldParkAfterFailedAcquire 方法我们可以知道:
            // 当前线程会把前驱线程的 waitStatus 值设置为 -1,代表唤醒
            // 当前驱节点获得锁并释放后,会根据这个 waitStatus 去判断是否需要唤醒后继线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 如果线程被挂起的过程中被中断过,会返回 true
                // 最终会在 acquire() 方法中调用 selfInterrupt() 方法,
                // 执行线程的 interrupt() 方法进行中断
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}
private final boolean parkAndCheckInterrupt() {
	// 将当前线程挂起
    LockSupport.park(this);
    // 返回当前线程的中断标识
    return Thread.interrupted();
}

1.3 总结

当我们使用 ReentrantLock 的时候,如果不指定,默认会使用非公平锁。并发大师 Doug Lea 将非公平锁作为默认的可重入锁的实现,这里比较侧重的是锁的获取效率,我们在实际使用时可以根据具体需求进行选择。

public ReentrantLock() {
    sync = new NonfairSync();
}

下面我们通过与公平锁的对比,来分析一下非公平锁的非公平性到底体现在哪里。

  • FairSync.lock() VS NonfairSync.lock()
final void lock() {
    acquire(1);
}
final void lock() {
	// 上来先去试着获取锁,不管锁是否被获取过
	// 不成功再说,万一成功了呢
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
  • FairSync.tryAcquire() VS Sync.nonfairTryAcquire()
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;
        }
    }
    // 如果锁没有被释放,判断当前线程是否是锁的 owner,如果是,直接重入
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {// 锁被释放
    	// 不管是否有前驱线程,直接去获取锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果锁没有被释放,判断当前线程是否是锁的 owner,如果是,直接重入
    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;
}

2. unlock()

2.1 时序图

AQS 源码解读系列--ReentrantLock 篇_第2张图片

2.2 源码解读

public void unlock() {
    sync.release(1);
}

AbstractQueuedSynchronizer.release()

public final boolean release(int arg) {
    if (tryRelease(arg)) {// 先去释放锁
        Node h = head;
        if (h != null && h.waitStatus != 0)
        	// 如果代表锁 owner 的头节点存在,并且 waitStatus 不为初始值,
        	// 唤醒后继节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}

Sync.tryRelease()

protected final boolean tryRelease(int releases) {
	// 重入的情况,进来几次就要退出几次,锁才能最终被释放
    int c = getState() - releases;
    // 当前线程不是锁的 owner,抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

AbstractQueuedSynchronizer.unparkSuccessor()

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)
        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;
    // 如果头节点的后继节点为空或者已经被取消
    if (s == null || s.waitStatus > 0) {
	    // 从尾节点向前遍历,找到最后一个需要被唤醒的节点,
	    // 即队列中的第一个没有被取消的节点,然后将其唤醒
	    // 这里有一个疑问,为什么要从前往后进行遍历?
	    // addWaiter() 和 enq() 两个方法中,节点入队的时候,
	    // 先执行的都是 node.prev = pred; 然后再通过 CAS 将当前节点设置成尾节点,
	    // 设置成功后再将前驱节点的后继节点设置为当前节点。
	    // 因此如果从前往后遍历,可能会出现断链的情况
        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);
}

2.3 总结

关于锁的释放,逻辑比较简单,没什么可说。这里说一下 AQS 中的 waitStatus 的含义,理解其含义对 AQS 体系的源码解读会有所帮助。
源码中对 waitStatus 的解释如下:

/** 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;// 标识当前线程等待在 condition 条件上
/**
 * waitStatus value to indicate the next acquireShared should
 * unconditionally propagate
 */
static final int PROPAGATE = -3;// 用于共享锁,标识传播共享标识

/**
 * Status field, taking on only the values:
 *   SIGNAL:     The successor of this node is (or will soon be)
 *               blocked (via park), so the current node must
 *               unpark its successor when it releases or
 *               cancels. To avoid races, acquire methods must
 *               first indicate they need a signal,
 *               then retry the atomic acquire, and then,
 *               on failure, block.
 *   CANCELLED:  This node is cancelled due to timeout or interrupt.
 *               Nodes never leave this state. In particular,
 *               a thread with cancelled node never again blocks.
 *   CONDITION:  This node is currently on a condition queue.
 *               It will not be used as a sync queue node
 *               until transferred, at which time the status
 *               will be set to 0. (Use of this value here has
 *               nothing to do with the other uses of the
 *               field, but simplifies mechanics.)
 *   PROPAGATE:  A releaseShared should be propagated to other
 *               nodes. This is set (for head node only) in
 *               doReleaseShared to ensure propagation
 *               continues, even if other operations have
 *               since intervened.
 *   0:          None of the above
 *
 * The values are arranged numerically to simplify use.
 * Non-negative values mean that a node doesn't need to
 * signal. So, most code doesn't need to check for particular
 * values, just for sign.
 *
 * The field is initialized to 0 for normal sync nodes, and
 * CONDITION for condition nodes.  It is modified using CAS
 * (or when possible, unconditional volatile writes).
 */
volatile int waitStatus;

你可能感兴趣的:(技术,菜鸟)