ReentrantLock

发布时间 2023-09-11 00:48:07作者: archaique

 

原理

1.非公平锁加锁流程(失败 park 并添加到阻塞链表)

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

lock()

// 非公平锁
static final class NonfairSync extends Sync {
        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
          // 加锁成功,设置锁 owner 为当前线程就结束了。直接返回
                setExclusiveOwnerThread(Thread.currentThread());
           // 加锁失败
            else
                acquire(1);
        }

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

acquire

会再试着加锁一次,如果再失败,就会进入等待队列

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

addWaiter 

cas 获取锁(!tryAcquire(arg))失败后:addWaiter 将等待的线程加入链表中

参数:Node.EXCLUSIVE 表示当前是独占模式,即只有一个线程能够访问资源

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            // 当前节点的 prev 指向之前的尾部节点 pred
            node.prev = pred;
            // CAS 设置链表 tail 变量为当前节点 node,期待前值为【pred】
            if (compareAndSetTail(pred, node)) {
                // 之前的尾部节点 pred 的 next 指向当前节点
                pred.next = node;
                return node;
            }
        }
        // pred==null 也就是链表没初始化
        // 或者 CAS 设置 tail 为当前节点失败时 进入
        enq(node);
        return node;
} 

enq

private Node enq(final Node node) {
      // 循环进行尝试
        for (;;) {
       // 每次循环获取最新的尾部节点
            Node t = tail;
       // 尾部节点为空,即链表还没初始化的情况
            if (t == null) { // Must initialize
                // CAS 将链表 head 变量设为当前节点,期待前值是 null
                if (compareAndSetHead(new Node()))
            // 新链表,tail 也为当前节点
                    tail = head;
            } 
        // addWaiter CAS 将链表的 tail 变量设为当前节点 失败时(也就是说 tail 被别的线程改掉了),会第一次走到这里,重新尝试
        else {
          // 当前节点 prev 指向最新的尾部节点 t
                node.prev = t;
          // CAS 将链表的 tail 变量设为当前节点,期待前值为 t
                if (compareAndSetTail(t, node)) {
             // 之前的尾部节点 t 的 next 指向当前节点
                    t.next = node;
            // 一定是添加到队列成功了,才会退出循环返回
return t; } } } }

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
          // 当前节点的前驱节点
                final Node p = node.predecessor();
          // 如果当前节点前一个节点是头节点,说明当前节点是等待队列里的第一个(头节点是虚的)
          // 此时再尝试获取一次锁 tryAcquire
if (p == head && tryAcquire(arg)) {             // 如果获取成功了。把当前节点设为虚头节点(令 head=当前节点,并且 thread prev 置为 null) setHead(node);             // 之前的头节点 p.next = null; // help GC             // 失败标志位 failed = false;             // 返回中断标志位 return interrupted; }           // 检查当前节点是否需要阻塞 if (shouldParkAfterFailedAcquire(p, node) &&             // 在这里 park 等待。 parkAndCheckInterrupt())             // interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

parkAndCheckInterrupt

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

shouldParkAfterFailedAcquire

注意不要混淆两个 state

state 是一个 AQS 实例中的一个属性,可以用 0 没被锁,1 加了锁

在等待队列中的节点都是没获取到锁的线程,Node 有一个属性叫 waitStaus,0 是初始状态

        /** 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;

检查是否需要阻塞

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
     // 获取前一个节点的状态
        int ws = pred.waitStatus;
     // 如果前一个节点在阻塞,所以这个也阻塞 park
        if (ws == Node.SIGNAL)
            return true;
     // >0 表示取消状态(>0的只有一个就是0取消)
if (ws > 0) { // 上一个节点取消, 那么重构删除前面所有取消的节点, 返回到外层循环重试 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; }
     // 前驱节点的 waitStatus 是【初始默认状态 0】,那么将前驱节点改为 -1。
     // -1 表示它有责任唤醒它的后继节点,因为要 park,所以必须保证可以有人来唤醒它,这个来唤醒它的就是前驱节点

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

可以看到失败后,还会进行好几次获取锁的尝试,在第四次失败后,才会真正 Park

每个等待线程由它的前驱节点线程唤醒,所以前驱节点的 waitStatus 都是 -1

 

2.非公平锁解锁流程()