java併發編程(十七)帶你瞭解什麼是面試官常說的AQS 一、初識AQS 二、 源碼分析 四、關於Condition的補充

前面我們學習了ReentrantLock,其底層就是用了AQS實現的,應該先講這一章節,但是當時給忘了,現在給補上吧。

關於ReentrantLock的學習,可以參考:https://www.jianshu.com/p/edec5185196d

AbstractQueuedSynchronizer是阻塞式鎖,以及同步器組件的實現框架。是JDK中實現併發編程的核心,它提供了一個基於FIFO隊列,平時我們工作中經常用到的ReentrantLock,CountDownLatch等都是基於它來實現的。

一、初識AQS

首先我們還是從前面學習的ReentrantLock入手,看看其代碼結構是什麼樣的:

從上圖可以看到以下幾點:

  • ReentrantLock實現接口Lock(抽象接口)
  • ReentrantLock有三個內部類,分別是FrairSync、NonfairSync、Sync,且FrairSync、NonfairSync繼承自Sync。
  • Sync繼承AbstractQueuedSynchronizer
  • AbstractQueuedSynchronizer有兩個內部類,分別是Node、ConditionObject。
  • AbstractQueuedSynchronizer繼承自AbstractOwnableSynchronizer(抽象類,提供獨佔線程的get和set)。

AQS有如下的特點:

  • 用 state 屬性來表示資源的狀態,包含獨佔狀態和共享狀態,對應公平鎖和非公平鎖。子類需要定義如何維護這個狀態,控制如何獲取鎖和釋放鎖,如上面圖中的關係,公平鎖和非公平鎖需要各自去維護這個state,達到獲取和釋放鎖的目的。

    • getState - 獲取 state 狀態
    • setState - 設置 state 狀態
    • compareAndSetState - 使用CAS設置狀態
    • 獨佔模式:只有一個線程能夠訪問資源
    • 共享模式:允許多個線程訪問資源
  • 提供了基於 FIFO 的等待隊列,類似於前面講Synchronized原理提到的 Monitor 的 EntryList

  • 使用條件變量來實現等待隊列、線程喚醒機制,同時支持多個條件變量,類似於前面講Synchronized原理提到的 Monitor 的 WaitSet

  • 【公平鎖】與【非公平鎖】:二者的區別主要在於獲取鎖是否和排隊順序有關。當鎖唄一個線程持有,其他嘗試獲取鎖的線程會被掛起,加到等待隊列中,先被掛起的在隊列的最前端。當鎖被釋放,需要通知隊列中的線程。作爲公平鎖,會先喚醒隊列最前端的線程;而非公平鎖會喚醒所有線程,通過競爭去獲取鎖,後來的線程有可能獲得鎖。

二、 源碼分析

下面我們通過源碼剖析其本質是什麼樣的。

首先在腦海中有個印象,AQS維護了兩個對個隊列,一個是同步隊列,一個是阻塞隊列。

Node可以說是【同步隊列】和【阻塞隊列】的節點。

2.1 Node源碼剖析

  static final class Node {
    // 模式,分爲共享與獨佔
    // 共享模式
    static final Node SHARED = new Node();
    // 獨佔模式
    static final Node EXCLUSIVE = null;        
    // 結點狀態
    // CANCELLED,值爲1,表示當前的線程被取消
    // SIGNAL,值爲-1,表示當前節點的後繼節點包含的線程需要運行,也就是unpark
    // CONDITION,值爲-2,表示當前節點在等待condition,也就是在condition隊列中
    // PROPAGATE,值爲-3,表示當前場景下後續的acquireShared能夠得以執行
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;        

    // 結點狀態
    volatile int waitStatus;        
    // 前驅結點
    volatile Node prev;    
    // 後繼結點
    volatile Node next;        
    // 結點所對應的線程
    volatile Thread thread;        
    // 下一個等待者
    Node nextWaiter;
    
    // 結點是否在共享模式下等待
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    
    // 獲取前驅結點,若前驅結點爲空,拋出異常
    final Node predecessor() throws NullPointerException {
        // 保存前驅結點
        Node p = prev; 
        if (p == null) // 前驅結點爲空,拋出異常
            throw new NullPointerException();
        else // 前驅結點不爲空,返回
            return p;
    }
    
    // 無參構造函數
    Node() {    // Used to establish initial head or SHARED marker
    }
    
    // 構造函數,被addWaiter使用
     Node(Thread thread, Node mode) {    // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }
    
    // 構造函數
    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

2.2 ConditionObject源碼剖析

實現了condition接口,關於condition的學習後面會介紹,在學習ReentrantLock時也介紹了其使用方式。

代碼較多,直接從上向下看吧:

 // 內部類
public class ConditionObject implements Condition, java.io.Serializable {
    // 版本號
    private static final long serialVersionUID = 1173984872572414699L;
    
    // condition隊列的頭結點
    private transient Node firstWaiter;
    
    // condition隊列的尾結點
    private transient Node lastWaiter;

    /**
     *  構造函數
     */
    public ConditionObject() { }

    /**
     * 添加新的waiter到wait隊列
     */
    private Node addConditionWaiter() {
        // 定義尾結點是t
        Node t = lastWaiter;
       // 尾結點不爲空,並且尾結點的狀態不爲CONDITION(默認是-2,表示當前節點在conditionObject等待隊列中)
        if (t != null && t.waitStatus != Node.CONDITION) { 
            // 清除狀態不爲CONDITION的結點,對firstWaiter和lastWaiter重新賦值
            unlinkCancelledWaiters(); 
            // 將最後一個結點重新賦值給t
            t = lastWaiter;
        }
        // 新建一個結點
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        // 尾結點爲空
        if (t == null) 
            // 設置condition隊列的頭結點
            firstWaiter = node;
        else 
            // 設置爲節點的nextWaiter域爲node結點
            t.nextWaiter = node;
        // 更新condition隊列的尾結點
        lastWaiter = node;
        return node;
    }

    /**
     *  移除或轉移頭結點到sync隊列,直到沒有取消的或者空爲止
     */
    private void doSignal(Node first) {
        // 循環
        do {
            // 將下一個節點設爲首節點,如果爲空
            if ( (firstWaiter = first.nextWaiter) == null) 
                // 設置尾結點爲空
                lastWaiter = null;
            // 設置first結點的nextWaiter域
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null); // 將結點從condition隊列轉移到sync隊列失敗並且condition隊列中的頭結點不爲空,一直循環
    }

    /**
     * 轉移所有等待隊列的節點到同步隊列
     */
    private void doSignalAll(Node first) {
        // condition隊列的頭結點尾結點都設置爲空
        lastWaiter = firstWaiter = null;
        // 循環
        do {
            // 獲取first結點的nextWaiter域結點
            Node next = first.nextWaiter;
            // 設置first結點的nextWaiter域爲空
            first.nextWaiter = null;
            // 將first結點從condition隊列轉移到sync隊列
            transferForSignal(first);
            // 重新設置first
            first = next;
        } while (first != null);
    }

    /**
     * 過濾掉狀態不爲CONDITION的節點
     * 對firstWaiter和lastWaiter重新賦值
     **/
    private void unlinkCancelledWaiters() {
        // 獲取condition隊列頭結點
        Node t = firstWaiter;
        // 獲取一個尾結點是null
        Node trail = null;
        while (t != null) {
            // 獲取下一個結點
            Node next = t.nextWaiter;
            // 頭結點的狀態不爲CONDTION狀態
            if (t.waitStatus != Node.CONDITION) { 
                // 設置t節點的下一個等待者爲空
                t.nextWaiter = null;
                if (trail == null) // trail爲空,首次進來一定爲空
                    // 重新設置condition隊列的頭結點
                    firstWaiter = next;
                else 
                    // 設置trail結點的nextWaiter域爲next結點
                    trail.nextWaiter = next;
                if (next == null) // next結點爲空
                    // 設置condition隊列的尾結點
                    lastWaiter = trail;
            }
            else // t結點的狀態爲CONDTION狀態
                // 設置trail結點
                trail = t;
            // 設置t結點
            t = next;
        }
    }

    /**
     * 實現Condition接口的signal方法
     */
    public final void signal() {
        if (!isHeldExclusively()) // 不被當前線程獨佔,拋出異常
            throw new IllegalMonitorStateException();
        // 保存condition隊列頭結點
        Node first = firstWaiter;
        if (first != null) // 頭結點不爲空
            // 喚醒一個等待線程,將頭結點從阻塞隊列移除,添加到同步隊列
            doSignal(first);
    }

    /**
     * 實現Condition的signalAll方法,喚醒所有線程
     */
    public final void signalAll() {
        if (!isHeldExclusively()) // 不被當前線程獨佔,拋出異常
            throw new IllegalMonitorStateException();
        // 保存condition隊列頭結點
        Node first = firstWaiter;
        if (first != null) // 頭結點不爲空
            // 喚醒所有等待線程,將頭結點從阻塞隊列移除,添加到同步隊列
            doSignalAll(first);
    }

    /**
     * 與await()區別在於,使用await方法,調用interrupt()中斷後會報錯,而該方法不會報錯。
     */
    public final void awaitUninterruptibly() {
        // 添加一個結點到等待隊列
        Node node = addConditionWaiter();
        // 獲取釋放的狀態
        int savedState = fullyRelease(node);
        boolean interrupted = false;
        while (!isOnSyncQueue(node)) { // 
            // 阻塞當前線程
            LockSupport.park(this);
            if (Thread.interrupted()) // 當前線程被中斷
                // 設置interrupted狀態
                interrupted = true; 
        }
        if (acquireQueued(node, savedState) || interrupted) // 
            selfInterrupt();
    }

    /**
     *  等待,當前線程在接到信號或被中斷之前一直處於等待狀態
     */
    public final void await() throws InterruptedException {
        // 當前線程被中斷,拋出異常
        if (Thread.interrupted()) 
            throw new InterruptedException();
        // 將當前線程包裝成Node,尾插入到等待隊列中
        Node node = addConditionWaiter();
        // 釋放當前線程所佔用的lock,在釋放的過程中會喚醒同步隊列中的下一個節點
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            // 當前線程進入到等待狀態
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) // 檢查結點等待時的中斷類型
                break;
        }
        // 自旋等待獲取到同步狀態(即獲取到lock)
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        // 處理被中斷的情況
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

    /**
     * 等待,當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態
     */
    public final long awaitNanos(long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        final long deadline = System.nanoTime() + nanosTimeout;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (nanosTimeout <= 0L) {
                transferAfterCancelledWait(node);
                break;
            }
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return deadline - System.nanoTime();
    }

    /**
     * 等待,當前線程在接到信號、被中斷或到達指定最後期限之前一直處於等待狀態
     */
    public final boolean awaitUntil(Date deadline)
            throws InterruptedException {
        long abstime = deadline.getTime();
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        boolean timedout = false;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (System.currentTimeMillis() > abstime) {
                timedout = transferAfterCancelledWait(node);
                break;
            }
            LockSupport.parkUntil(this, abstime);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return !timedout;
    }

    /**
     * 等待,當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態。此方法在行爲上等              
     * 效於:awaitNanos(unit.toNanos(time)) > 0
     */
    public final boolean await(long time, TimeUnit unit)
            throws InterruptedException {
        long nanosTimeout = unit.toNanos(time);
        if (Thread.interrupted())
            throw new InterruptedException();
        // 1. 將當前線程包裝成Node,尾插入到等待隊列中
        Node node = addConditionWaiter();
        // 2. 釋放當前線程所佔用的lock,在釋放的過程中會喚醒同步隊列中的下一個節點
        int savedState = fullyRelease(node);
        final long deadline = System.nanoTime() + nanosTimeout;
        boolean timedout = false;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (nanosTimeout <= 0L) {
                timedout = transferAfterCancelledWait(node);
                break;
            }
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return !timedout;
    }

2.3 鎖的獲取和釋放

整個AQS的設計理念就是通過state字段來實現鎖的獲取和釋放,鎖有主要分爲公平鎖和非公平鎖。

2.3.1 公平鎖

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            // 繼承自AQS的方法,內部先調用tryAcquire獲取鎖,獲取失敗則添加下城到等待隊列當中
            acquire(1);
        }

        /**
         * 公平鎖版本的tryAcquire
         */
        protected final boolean tryAcquire(int acquires) {
            // 獲取當前線程
            final Thread current = Thread.currentThread();
            // 獲取鎖的狀態
            int c = getState();
            // 0表示鎖沒有被持有
            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;
        }
    }

2.3.2 非公平鎖

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * 立即獲取鎖,失敗會加入等待隊列
         */
        final void lock() {
            // 通過CAS嘗試獲取鎖,成功則設置當前線程獨佔
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 否則加入等待隊列·····································································································································
                acquire(1);
        }

        /** 
          * 非公平鎖版本的tryAcquire
          */
        protected final boolean tryAcquire(int acquires) {
            // 走其父類Sync的默認nonfairTryAcquire
            return nonfairTryAcquire(acquires);
        }
    }

2.3.3 Syc子類

這是公平鎖和非公平鎖的父類,提供統一的tryRelease方法釋放鎖

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 提供非公平版本的快捷路徑
         */
        abstract void lock();

        /**
         * 非公平鎖獲取,默認就是非公平鎖
         */
        final boolean nonfairTryAcquire(int acquires) {
            // 獲取當前線程
            final Thread current = Thread.currentThread();
            // 獲取當前鎖的狀態
            int c = getState();
            // 0表示沒有被佔用
            if (c == 0) {
                // CAS佔用,成功則設置當前線程爲獨佔鎖
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 當前線程是獨佔鎖,表示鎖重入
            else if (current == getExclusiveOwnerThread()) {
                // 狀態 + 1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 設置當前狀態
                setState(nextc);
                return true;
            }
            return false;
        }

         /**
         * 釋放鎖
         */
        protected final boolean tryRelease(int releases) {
            // 當前狀態 減去 釋放的數量
            int c = getState() - releases;
            // 如果當前線程不是佔有鎖的線程,拋出異常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 當全部釋放後,狀態爲0,取消獨佔線程
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            // 設置狀態爲0,返回釋放成功
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            // 當前線程是否是鎖持有者
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // 獲取當前持有者
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        // 獲取持有數,只有是線程持有者才能獲取
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
    }

3.3.4 acquire 和 release

在AQS當中還有兩個核心方法:

  • acquire():獲取鎖,這是實際鎖調用上鎖的真正方法,前面的try開頭的知識嘗試獲取鎖,即使失敗也不會添加到等待隊列。
    public final void acquire(int arg) {
      // 嘗試獲取
      if (!tryAcquire(arg) &&
          // 嘗試獲取成功,以獨佔方式添加到等待隊列,並不斷地嘗試佔有鎖知道成功
          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
          selfInterrupt();
  }
  • release():釋放鎖,這是實際釋放鎖的方法,會調用鎖自定義的同步器實現的tryRelease方法:
        /**
          * 嘗試釋放,成功後返回true
          */
        public final boolean release(int arg) {
          if (tryRelease(arg)) {
              Node h = head;
              if (h != null && h.waitStatus != 0)
                  unparkSuccessor(h);
              return true;
          }
          return false;
      }
    

後面我們自定義不可重入鎖,來看看同步器和鎖的關係是什麼樣的,加深理解。

2.4 簡單總結

到此爲止,通過閱讀前面的源碼內容,我們可以有如下的總結:

  • 鎖的釋放和獲取都是圍繞 【state】來做的,0表示未持有鎖,1表示獨佔,大於一表示鎖重入
  • 獲取鎖的姿勢如下:
      // 如果獲取鎖失敗
      if (!tryAcquire(arg)) {
        // 入隊, 可以選擇阻塞當前線程 park unpark
      }
    
  • 釋放鎖的姿勢如下:
      // 如果獲取鎖成功
      if (!tryRelease(arg)) {
        // 讓阻塞線程恢復運行
      }
    

三、實踐

瞭解了AQS的結構之後,我們不妨自己動手實踐一番。加深理解。

定義一個不可重入鎖,需要一個同步器,一個鎖,一個測試類

自定義同步器:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * @description: 實現一個不可重入鎖 同步器,state最大隻能是1
 * @author:weirx
 * @date:2022/1/13 13:49
 * @version:3.0
 */
public class MyLockSynchronizer extends AbstractQueuedSynchronizer {

    @Override
    protected boolean tryAcquire(int acquires) {
        int state = getState();
        if (state == 0) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int acquires) {
        int c = getState() - acquires;
        if (c == 0) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        return false;
    }

    @Override
    protected boolean isHeldExclusively() {
        return getState() == 1;
    }

    protected ConditionObject newCondition() {
        return new ConditionObject();
    }
}

自定義鎖:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @description: 自定義鎖
 * @author:weirx
 * @date:2022/1/13 14:05
 * @version:3.0
 */
public class MyLock implements Lock {

    MyLockSynchronizer myLockSynchronizer = new MyLockSynchronizer();

    @Override
    public void lock() {
        // 嘗試獲取鎖,失敗則加入等待隊列
        myLockSynchronizer.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        // 嘗試獲取鎖,失敗則加入等待隊列, 可中斷
        myLockSynchronizer.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        // 嘗試獲取鎖,不加入等待隊列
        return myLockSynchronizer.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        // 嘗試獲取鎖,不加入等待隊列,有實現
        return myLockSynchronizer.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        // 釋放鎖
        myLockSynchronizer.release(1);
    }

    @Override
    public Condition newCondition() {
        // 條件變量
        return myLockSynchronizer.newCondition();
    }
}

測試鎖的效果:

/**
 * @description: 測試
 * @author:weirx
 * @date:2022/1/13 14:24
 * @version:3.0
 */
public class TestMyLock {

    public static void main(String[] args) {
        MyLock myLock = new MyLock();

        new Thread(() -> {
            try {
                myLock.lock();
                System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                        + " " + Thread.currentThread().getName() + " :acquire lock success");

                // 休眠一秒看效果
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                myLock.unlock();
                System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                        + " " + Thread.currentThread().getName() + " :release lock success");
            }

        }, "t1").start();

        new Thread(() -> {
            try {
                myLock.lock();
                System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                        + " " + Thread.currentThread().getName() + " :acquire lock success");
            } finally {
                myLock.unlock();
                System.out.println(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                        + " " + Thread.currentThread().getName() + " :release lock success");
            }
        }, "t2").start();
    }
}

結果,ti一秒後釋放鎖,纔會由t2獲得鎖:

2022-01-13 14:34:56 t1 :acquire lock success
2022-01-13 14:34:57 t2 :acquire lock success
2022-01-13 14:34:57 t1 :release lock success
2022-01-13 14:34:57 t2 :release lock success

測試下不可重入是否好使,只需要在上述測試代碼的線程t1中,再次使用myLock.lock()獲取一次鎖,發現,整個程序被卡住了,只會打印一條信息:

2022-01-13 14:35:56 t1 :acquire lock success

四、關於Condition的補充

本篇沒有介紹Condition的具體內容,但是在之前講解ReentrantLock提到過【條件變量】,可以返回去看這篇文章瞭解其用法:https://www.jianshu.com/p/edec5185196d


源碼學習真是難,看別人說的再多不如自己跟着走一遍,建議同學們參照本文自己跟蹤一遍。

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