ReentrantLock原理分析

心動如雷發表於2020-06-08

一 UML類圖

1.1、ReentrantLock 

通過類圖ReentrantLock是同步鎖,同一時間只能有一個執行緒獲取到鎖,其他獲取該鎖的執行緒會被阻塞而被放入AQS阻塞佇列中。ReentrantLock類繼承Lock介面;內部抽象類Sync實現抽象佇列同步器AbstractQueuedSynchronizer;Sync類有兩個子類NonfairSync(非公平鎖)和FairSync(公平鎖),預設構造方法使用非公平鎖,可以使用帶布林引數的構造方法指定使用公平鎖;ReentrantLock可以建立多個條件進行繫結。

1.2、AbstractQueuedSynchronizer

AbstractQueuedSynchronizer:抽象佇列同步器,維護一個volatile int state變數標識共享資源和一個FIFO執行緒阻塞佇列(AQS佇列)。

protected final void setState(int newState):設定state值

protected final int getState():獲取state值

protected final boolean compareAndSetState(int expect, int update):CAS設定state值

AQS有兩種共享資源型別:SHARED(共享)和EXCLUSIVE(獨佔),針對型別有不同的方法:

protected boolean tryAcquire(int arg):獨佔型別獲取鎖

protected boolean tryRelease(int arg):獨佔型別釋放鎖

protected int tryAcquireShared(int arg):共享型別獲取鎖

protected boolean tryReleaseShared(int arg):共享型別釋放鎖

protected boolean isHeldExclusively():是否是獨佔型別

1.3、執行緒節點型別waitStatus

AQS佇列中節點的waitStatus列舉值(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node)含義:

 static final int CANCELLED = 1; //執行緒被取消

static final int SIGNAL = -1; //成功的執行緒需要被喚醒
static final int CONDITION = -2; //執行緒在條件佇列中等待
static final int PROPAGATE = -3; //釋放鎖是需要通知其他節點

二 原理分析

2.1 獲取鎖

2.1.1 void lock()方法

呼叫執行緒T呼叫該方法嘗試獲取當前鎖。

①如果鎖未被其他執行緒獲取,則呼叫執行緒T成功獲取到當前鎖,然後設定當前鎖的擁有者為呼叫執行緒T,並設定AQS的狀態值state為1,然後直接返回。

②如果呼叫執行緒T之前已經獲取當前鎖,則只設定設定AQS的狀態值state加1,然後返回。

③如果當前鎖已被其他執行緒獲取,則呼叫執行緒T放入AQS佇列後阻塞掛起。

public void lock() {
    sync.lock();//委託內部公平鎖和非公平鎖獲取鎖
} 
//非公平鎖
final
void lock() { if (compareAndSetState(0, 1))//設定AQS狀態值為1 setExclusiveOwnerThread(Thread.currentThread());//成功設定鎖的執行緒擁有者 else acquire(1);//失敗加入到AQS佇列阻塞掛起 } //公平鎖 final void lock() { acquire(1); }

非公平鎖分析:

//呼叫父類AbstractOwnableSynchronizer方法CAS設定state值,成功返回true,失敗返回false
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//呼叫父類AbstractOwnableSynchronizer方法,設定當前執行緒為鎖的擁有者
protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}
//呼叫AbstractQueuedSynchronizer父類方法,第一次獲取鎖失敗
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//排它鎖型別
        selfInterrupt();
}
//NonfairSync子類,重寫父類方法
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
//Sync類非公平鎖嘗試獲取鎖
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {//二次獲取鎖
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {//當前執行緒已獲取鎖,AQS狀態值加1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
//AbstractQueuedSynchronizer類建立節點,新增到AQS佇列後面
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);//建立AQS佇列的節點,節點型別排它鎖
    Node pred = tail;//尾結點
    if (pred != null) {
        node.prev = pred;//新節點的前一個節點是尾結點
        if (compareAndSetTail(pred, node)) {//CAS機制新增節點
            pred.next = node;//尾結點執行新的節點
            return node;
        }
    }
    enq(node);
    return node;
}
//插入節點到佇列中
private
Node enq(final Node node) { for (;;) {//迴圈的方式,直至建立成功 Node t = tail;//尾結點 if (t == null) { //尾結點為空,初始化 if (compareAndSetHead(new Node()))//第一步:CAS建立頭結點(哨兵節點)一個空節點 tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) {//第二步:CAS設定尾結點 t.next = node; return t; } } } }
//
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)) {//如果p節點是頭結點,node作為佇列第二個節點
                setHead(node);//將頭節點設定為node節點,node節點出佇列
                p.next = null; //原頭結點設定為空,便於GC回收
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);//失敗解鎖
    }
}
private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}
//阻塞掛起當前執行緒
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}
//
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);//大於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(); }

公平鎖分析:

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;
        }
    }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;
  //①h != t:表示AQS佇列頭結點和尾結點不相同,佇列不為空;
  //②(s = h.next) == null || s.thread != Thread.currentThread():頭結點(哨兵節點)為空或者next節點不等於當前執行緒
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }

 2.1.2 void lockInterruptibly()方法

與2.2.1方法相似,不同之處在於:該方法對中斷進行響應,其他執行緒呼叫當前執行緒中斷方法,丟擲InterruptedException。

2.1.3 boolean tryLock()方法

嘗試獲取鎖。注意:該方法不會引起當前執行緒阻塞。

2.1.4 boolean tryLock(long timeout, TimeUnit unit)方法

與2.1.3方法相似,不同之處在於:設定了超時時間。

2.2 釋放鎖

嘗試釋放鎖。

如果當前執行緒T已獲取鎖,則呼叫該方法更新AQS狀態值減1。如果當前狀態值為0,則釋放鎖;如果當前狀態值部位0,則只是減1操作。

如果當前執行緒T未獲取鎖,則呼叫該方法是會丟擲IllegalMonitorStateException異常。

2.2.1 void unlock()方法

public void unlock() {
    sync.release(1);
}
//呼叫AbstractQueuedSynchronizer方法
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//喚醒執行緒
        return true;
    }
    return false;
}
//回撥Sync類釋放鎖
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 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)
        compareAndSetWaitStatus(node, ws, 0);//CAS設定狀態

    /*
     * 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) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)//遍歷AQS佇列
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);//喚醒執行緒
}

 

h != t

相關文章