【線程】ReentrantLock 內部公平鎖與非公平鎖實現 (十)

我的原則:先會用再說,內部慢慢來


一、概念

  1. 公平鎖: 遵循FIFO,先來先服務的原則。
  2. 非公平鎖: 哄搶鎖,誰先搶到服務誰。

二、ReentrantLock 架構

在這裏插入圖片描述

三、FailSync 與 NonfairSync 核心代碼區別

  1. 根據 AQS 實現類的不同,FairSync + NonfairSync 的tryAcquire實現不同
  2. Sync#nonfairTryAcquire 非公平模式獲取鎖方法
  3. FairSync#tryAcquire 公平模式獲取鎖方法
  1. AbstractQueuedSynchronizer#acquire 方法
public final void AbstractQueuedSynchronizer#acquire(int arg) {
	/* 
	 * 根據 AQS 實現類的不同,FairSync + NonfairSync 的tryAcquire實現不同
	 * 1. NonfaireSync 在 tryAcquire 很大機率獲得鎖 ,不往下走了
	 * 2。 FairSync 在 tryAcquire ,若遇到Sync隊列有Node,那麼就直接獲取失敗,進入 acquireQueued 去排隊。
	 */
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  1. Sync#nonfairTryAcquire 非公平模式獲取鎖方法
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    	// 公平鎖與非公平鎖區別就在這,非公平鎖模式下,一會機會直接就去爭奪鎖了。無需FIFO
        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");
        setState(nextc);
        return true;
    }
    return false;
}
  1. FairSync#tryAcquire 公平模式獲取鎖方法
protected final boolean FairSync#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;
}
  1. 解釋一下代碼,在隊列有一排Node的情況下,爲什麼 c == 0 ?

A1 拿到鎖還沒處理完,A2 - AN 阻塞當中,Sync 隊列裏面包含 A2 - AN,此時,A1剛好處理完,currentThread = null,B1 剛好來 lock.lock ,這時候 c 就是 0,那麼接下去怎麼處理。

  1. NonfaireSync模式下 直接搶,不管你們那些排隊的
  2. FairSync 模式下,退出去排隊。

四、代碼 demo

  1. FairSync 模式
public class _12_02_TestCondition {
	public static void main(String[] args) throws Exception{
		LockDemo lockDemo = new LockDemo();
		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				try {
					lockDemo.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			},"A" + i).start();
		}
		// 確保上面代碼先執行
		Thread.sleep(1000);
		Scanner sc = new Scanner(System.in);
		System.out.println("點擊任意鍵喚醒線程 ...");
		sc.nextLine();

		for (int i = 0; i < 5; i++) {
			new Thread(() -> {
				lockDemo.signal();

			},"B" + i).start();
		}
	}
	static class LockDemo{
		ReentrantLock lock = new ReentrantLock(true);
		Condition condition = lock.newCondition();
		public void await() throws Exception{
			System.out.println(Thread.currentThread().getName() + ",condition ready to lock ===");
			lock.lock();
			System.out.println(Thread.currentThread().getName() + ",condition ready to await ");
			condition.await();
			System.out.println(Thread.currentThread().getName() + ",condition ready to unlock ===");
			lock.unlock();
		}
		public void signal(){
			System.out.println(Thread.currentThread().getName() + ",condition ready to lock ");
			lock.lock();
			System.out.println(Thread.currentThread().getName() + ",condition ready to signal ");
			condition.signal();
			System.out.println(Thread.currentThread().getName() + ",condition ready to unlock ");
			lock.unlock();
		}
	}
}
    1. FairSync 模式下輸出:
A0,condition ready to lock ===
A1,condition ready to lock ===
A0,condition ready to await 
A2,condition ready to lock ===
A3,condition ready to lock ===
A4,condition ready to lock ===
A1,condition ready to await 
A2,condition ready to await 
A3,condition ready to await 
A4,condition ready to await 
點擊任意鍵喚醒線程 ...


B0,condition ready to lock 
B0,condition ready to signal 
B0,condition ready to unlock 
B1,condition ready to lock 
A0,condition ready to unlock ===
B2,condition ready to lock 
B1,condition ready to signal 
B1,condition ready to unlock 
B3,condition ready to lock 
B2,condition ready to signal 
B2,condition ready to unlock 
A1,condition ready to unlock ===
B4,condition ready to lock 
B3,condition ready to signal 
B3,condition ready to unlock 
A2,condition ready to unlock ===
B4,condition ready to signal 
B4,condition ready to unlock 
A3,condition ready to unlock ===
A4,condition ready to unlock ===
  1. 改成 NonfairSync 模式

ReentrantLock lock = new ReentrantLock(); // 默認非公平鎖

  1. NonfairSync 模式下輸出
A0,condition ready to lock ===
A1,condition ready to lock ===
A2,condition ready to lock ===
A0,condition ready to await 
A3,condition ready to lock ===
A3,condition ready to await 
A1,condition ready to await 
A2,condition ready to await 
A4,condition ready to lock ===
A4,condition ready to await 
點擊任意鍵喚醒線程 ...


B0,condition ready to lock 
B1,condition ready to lock 
B0,condition ready to signal 
B0,condition ready to unlock 
B2,condition ready to lock 
B2,condition ready to signal 
B2,condition ready to unlock 
B3,condition ready to lock 
B1,condition ready to signal 
B4,condition ready to lock 
B1,condition ready to unlock 
A0,condition ready to unlock ===
A3,condition ready to unlock ===
B3,condition ready to signal 
B3,condition ready to unlock 
A1,condition ready to unlock ===
B4,condition ready to signal 
B4,condition ready to unlock 
A2,condition ready to unlock ===
A4,condition ready to unlock ===

結論:

ReentrantLock lock = new ReentrantLock(true); 公平鎖模式下,誰先 lock ,誰就先 unlock。
ReentrantLock lock = new ReentrantLock(); 公平鎖模式下,不一定。

四、番外篇

下一章節:【線程】ReentrantReadWriteLock 內部共享鎖與排他鎖源碼分析 (十一)
上一章節:【線程】ReentrantLock + Condition 源碼剖析 (九)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章