乐趣区

关于java:ReentrantLock原码学习二公平锁

ReentrantLock 原码学习:偏心锁

书接上文,偏心锁和非偏心锁的不同,次要体现在锁的竞争形式不同。

偏心锁在获取锁的时候,同样分三种状况:第一种:在查到以后锁状态为 0 的时候(即无线程持锁),并不会立刻去竞争锁,而是先通过 hasQueuedPredecessors() 办法判断 CLH 锁队列中是否还有期待已久的线程,如果有,那么以后线程乖乖去锁队列的尾部去排队,考究个先来后到;如果队列中没有期待线程,那么以后线程就去竞争锁,胜利后,将以后线程设置为锁的持有者;如果锁状态不为 0,判断是不是本人持有了锁,如果是,则批改锁状态加 1,这就是锁重入;第三种,锁被占有,但不是本人持有,那么乖乖去 CLH 锁队列尾部排队。

        protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            // 判断以后的 CLH 锁队列中是否还有排在后面的线程
                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;
        }

锁的开释

无论是偏心锁还是非偏心锁,开释锁的形式都是对立的,通过批改 AQS 中锁状态 state 的值(每次开释减 1)来开释,当 state 为 0 时,将 AQS 中 ExclusiveOwnerThread 置为 null,示意以后线程开释掉锁。

rotected final boolean tryRelease(int releases) {int c = getState() - releases;
            // 判断开释锁的线程是否是持锁线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 锁状态为 0,则将 ExclusiveOwnerThread 置空,锁被开释
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c); // 批改锁状态,return free;
        }

开释锁胜利后,须要唤醒 CLH 锁队列中的后继节点。这里由父类 AQS 中实现:

public final boolean release(int arg) {if (tryRelease(arg)) {
            Node h = head;
            // 如果 HEAD 节点存在,期待状态不是初始化状态,就唤醒 HEAD 的后继节点
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

偏心锁与非偏心锁总结

  • 无论是偏心锁还是非偏心锁,都是可重入锁,判断持锁线程是本人后,批改锁状态 state,且在第一次获取锁时,ExclusiveOwnerThread 置为以后线程;
  • 开释锁的时候,也是批改锁状态 state 的值,为 0 的时候,ExclusiveOwnerThread 置空,示意锁开释,同时会唤醒 AQS 的锁期待队列中,HEAD 节点的后继节点(waitStatus<=0, 未勾销的节点);
  • 偏心锁在竞争锁的时候,会先判断 CLH 锁队列中是否有期待已久的线程,有,则放弃本次竞争,作为尾节点插入 CLH 队列期待唤醒,考究先来后到;
  • 非偏心锁在竞争锁的准则是:只有以后锁未被持有,就去竞争,不必管队列里是否有线程期待;
退出移动版