ReentrantLock源碼簡單分析

ReentrantLock 能用於更精細化的加鎖的Java類, 通過它能更清楚瞭解Java的鎖機制

ReentrantLock 類的集成關係有點複雜, 既有內部類, 還有多重繼承關係

在這裏插入圖片描述### 類的定義

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
  ..............
  • 實現了 Serializable 接口
  • 實現了Lock接口, Lock 接口中就定義常用的加鎖和釋放鎖的方法. 是一個基本的接口, 很多的鎖類都實現了這個方法
  • sync是它的重要成員變量, 加鎖和解鎖的操作都是通過這個變量實現的, 這個Sync 是一個靜態內部類.

加鎖的邏輯

ReentrantLock 的lock方法, tryLock方法和unlock方法

public void lock() {
    sync.lock();
}
public void unlock() {
    sync.release(1);
}
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

從上面的方法可以看出加鎖的操作都是交給Sync類來實現的,下面就來看看Sync類

構造方法

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

兩種構造方法其實是對應這兩種鎖, 公平鎖和非公平鎖, 公平鎖是依賴FairSync 類來實現的, 非公平鎖是依賴NonfairSync來實現的

NonfairSync類詳解, 非公平獲取鎖的真正操作類

靜態內部final類

/**
 * Sync object for non-fair locks
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

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

NonfairSync類實現了虛擬類Sync 的lock 和tryAcquire 方法

而Sync 又繼承了AbstractQueuedSynchronizer 虛擬類, AbstractQueuedSynchronizer 是一個很重要的類內部維護了 等待獲取鎖的線程隊列

lock方法

加鎖方法, 方法內有兩個分支邏輯,

  1. 先判斷是否是無鎖狀態並嘗試加鎖compareAndSetState , 如果無鎖且加鎖成功則把當前線程加入AQS隊列中setExclusiveOwnerThread.
  2. 嘗試獲取鎖沒有成功, 就調用acquire 方法來獲取鎖

compareAndSetState方法判斷

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

內部使用unsafe.compareAndSwapInt方法, 這方法是native方法. 是把比較和設置兩步作爲原子操作的方法.

setExclusiveOwnerThread方法

這個方法就是 AbstractQueuedSynchronizer 類的方法, 左右就是把當前線程標記成獲取鎖的線程

acquire方法, 真正獲取鎖的方法.

這個是NonfairSync 父類的父類AbstractQueuedSynchronizer 類的方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果獲取到鎖就會跳出這個循環, 如果獲取不到鎖, 會一直阻塞在這裏
        selfInterrupt();
}

內部一個if判斷後的調用selfInterrupt方法. 重點在if判斷中邏輯.

if中首先調用tryAcquire方法, tryAcquire 在AbstractQueuedSynchronizer 中 沒有實現邏輯只是拋出異常, 所以具體的邏輯實在子類NonfairSync 中,

NonfairSync.tryAcquire方法

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

可以看到調用了nonfairTryAcquire 方法, nonfairTryAcquire方法又在Sync類中實現

Sync.nonfairTryAcquire方法如下

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();  //獲取當前線程
    int c = getState();  // 獲取當前鎖的計數器
    if (c == 0) {  // 計數器0,說明當前鎖是空閒的
        if (compareAndSetState(0, acquires)) { //比較並獲取鎖  
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {  // 當前線程就是已經獲取鎖的線程
        int nextc = c + acquires;  // 直接計數器上加 acquires, 傳入的是1 
        if (nextc < 0) //   計數器數字異常
            throw new Error("Maximum lock count exceeded");
        setState(nextc);  // 設置計數器的數值.  這裏可以直接設置, 因爲當前線程就是已經獲取鎖的線程,  可重入鎖就是體現在這的
        return true;
    }
    return false; //  獲取鎖失敗
}
  • 這個方法的作用, 就是看下鎖的狀態是否可以直接獲取鎖, 兩種情況可以直接獲取鎖
    • 鎖是空閒狀態, 沒有線程獲取鎖
    • 當前線程就是獲取鎖的線程, 直接計數器加值表示重複獲取鎖

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,

這個也是AbstractQueuedSynchronizer 的方法. 這個方法纔是真正的自旋阻塞獲取鎖的方法

/**
 * Acquires in exclusive uninterruptible mode for thread already in
 * queue. Used by condition wait methods as well as acquire.
 *
 * @param node the node
 * @param arg the acquire argument
 * @return {@code true} if interrupted while waiting
 */
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)) {  // tryAcquire 就是嘗試獲取一下鎖
                setHead(node);     
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • node.predecessor(); 就是獲取當前線程的上一個線程
  • 如果當前線程獲取鎖成功, 就把當前線程設置成隊列的頭部, 並且方法返回false
  • 如果當前線程獲取鎖失敗, 就進入shouldParkAfterFailedAcquire 方法, shouldParkAfterFailedAcquire內部會判斷當前線程的上個線程的狀態標記, 如果標記是<0(標識有問題) 就把上個線程移除隊列, 如果標識是 SIGNAL就返回true, 如果是其他的就設置成SIGNAL 並且返回false. SIGNAL 標識線程阻塞中
  • parkAndCheckInterrupt方法會判斷當前線程是否應該中斷,
  • finally 中的方法 在正常獲取到鎖的時候回運行, cancelAcquire 中把當前線程和前面的線程都移除隊列
  • 其中addWaiter(Node.EXCLUSIVE) 方法就是把當前線程封裝成Node, 並且把這個Node增加在隊列的尾部
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;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) { // 把當前線程 比較並設置 成線程對列的尾部
            pred.next = node;
            return node;
        }
    }
  // 設置失敗就走到這裏了
    enq(node);  //這個方法內部進行循環  比較並設置 成線程隊列的尾部.    如果隊列還是空的, 就new一個新的Node設置在頭部  
    return node;
}

到這裏 就是加鎖邏輯全部走完

解鎖的邏輯

unlock解鎖方法

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

release方法在是在AQS中實現的

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

tryRelease 解鎖方法

是在Sync中實現的

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;  //當前鎖計數器 減去一些
    if (Thread.currentThread() != getExclusiveOwnerThread())  //如果當前線程不是獲取鎖的線程,直接拋出異常
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {  //此次釋放後, 計數器等於0. 
        free = true;
        setExclusiveOwnerThread(null); // 設置隊列中鎖線程標記爲null
    }
    setState(c);  // 設置新的計數器
    return free;
}
  • 釋放鎖的邏輯很簡單

unparkSuccessor方法

在釋放鎖成功後, 會進行這個方法. 這個方法會把後續的線程喚醒. LockSupport.unpark(s.thread); 就是喚醒線程方法

private void unparkSuccessor(Node node) {
	// 將狀態設置爲同步狀態
	int ws = node.waitStatus;
	if (ws &lt; 0) 		compareAndSetWaitStatus(node, ws, 0); 	// 獲取當前節點的後繼節點,如果滿足狀態,那麼進行喚醒操作 	// 如果沒有滿足狀態,從尾部開始找尋符合要求的節點並將其喚醒 	Node s = node.next; 	if (s == null || s.waitStatus &gt; 0) {
		s = null;
		for (Node t = tail; t != null &amp;&amp; t != node; t = t.prev)
			if (t.waitStatus &lt;= 0)
				s = t;
		}
	if (s != null)
		LockSupport.unpark(s.thread);
}

FairSync公平鎖的邏輯

lock方法

加鎖邏輯, 與NoFairSync區別是直接

final void lock() {
    acquire(1);
}

acquire方法與非公平鎖的方法一樣

tryAcquire方法

這個方法與NonfairSync中tryAcquire方法有區別的, NonfairSync中的tryAcquire 是調用了父類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;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);  //如果當前線程就是已經獲取鎖的線程, 就直接設置計數器值
        return true;
    }
    return false;  
}
  • hasQueuedPredecessors方法中的邏輯很繞, hasQueuedPredecessors 判斷當前線程在隊列中的第二位, 是返回false, 否則返回true
  • 是第二位, 則進行compareAndSetState 方法, 比較並設置, 如果鎖沒有線程獲取就嘗試獲取. 獲取成功就標記當前線程爲獲取鎖線程
  • 如果state 不是0 , 表示已經有線程獲取鎖了, if (current == getExclusiveOwnerThread()) 判斷當前線程是否已經是獲取鎖的線程.
  • 如果最終還是沒有獲取鎖成功, 就返回false

公平鎖和非分公平鎖的獲取鎖的區別

Lock方法中的區別

  1. 公平鎖中lock 會直接進入acquire 方法, 會直接進入隊列中獲取鎖
  2. 非公平鎖, 會先嚐試下判斷當前線程是否已經獲取鎖, 獲取鎖計數器0 嘗試獲取下, 獲取失敗纔會進入acquire 方法

tryAcquire方法中的區別, 這個方法纔是真正的一次獲取鎖的方法,

  1. 公平鎖在compareAndSetState之前會調用下hasQueuedPredecessors 方法, 判斷下當前節點是否是第二節點. 是第二節點纔會獲取鎖
  2. 非公平鎖 沒有hasQueuedPredecessors判斷.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章