日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ReentrantLock 源码分析

發布時間:2024/9/30 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ReentrantLock 源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ReentrantLock簡單使用demo如下:

Lock lock = new ReentrantLock(); lock.lock(); try {//業務邏輯 } finally {lock.unlock(); }

注:獲取的鎖代碼要放到try塊之外,防止獲得鎖代碼異常,拋出異常的同時,也會導致一次鎖的釋放。釋放代碼一定要放到finally塊中。

AQS
了解java中的鎖,首先的了解AQS。
AQS(AbstractQueuedSynchronizer)隊列同步器。是用來構建鎖或者其它同步組件的基礎框架,他實現了一個int成員變量標識同步狀態(更改這個變量值來獲取和釋放鎖),通過內置的FIFO雙向隊列來完成資源獲取線程排隊的工作。
AQS可以實現獨占鎖和共享鎖,RenntrantLock實現的是獨占鎖,ReentrantReadWriteLock實現的是獨占鎖和共享鎖,CountDownLatch實現的是共享鎖。

ReentrantLock 類結構信息如下圖:

  • ReentrantLock 實現 Lock 和 Serializable 接口
  • RentrantLock 有三個內部類 Sync、NonfairSync 和 FairSync 類
  • Sync 繼承 AbstractQueuedSynchronizer 抽象類
  • NonfairSync(非公平鎖) 繼承 Sync 抽象類
  • FairSync(公平鎖) 繼承 Sync 抽象類

公平鎖和非公平鎖

ReentrantLock 有兩種實現方式,公平鎖和非公平鎖。

  • 公平鎖:當前線程不立刻獲得鎖,而是先直進入等待隊列中隊尾進行排隊獲取鎖。

  • 非公平鎖:當前線程首先嘗試獲取一下鎖(僅僅嘗試一下),如果獲取不到,則乖乖的進入到等待隊列中去排隊。

ReentrantLock實現公平鎖和非公平鎖代碼如下:

/*** 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();}

* 獲取非公平鎖 *

/*** Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}
  • 首先通過CAS更新AQS中的state變量來獲得鎖(第一次獲得鎖),如果獲取成功則把當前線程設置為獨占鎖
  • 如果是設置失敗,進入到acquire方法
  • public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
  • 首先執行tryAcquire方法,嘗試獲得鎖。
  • 如果獲取失敗則進入addWaiter方法,構造同步節點,將該節點添加到同步隊列尾部,并返回此節點,進入acquireQueued方法。
  • acquireQueued方法,這個新節點死是循環的方式獲取同步狀態,如果獲取不到則阻塞節點中的線程,阻塞后的節點等待前驅節點來喚醒。
  • /*** Performs non-fair tryLock. tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/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()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

    tryAcquire調用nonfairTryAcquire方法來第二次嘗試獲得鎖
    1. 如果state變量為0,則進行CAS嘗試更新state來獲得鎖,并把該線程設置成獨占鎖,并返回true。
    2. 如果state變量不為0,則判斷當前線程是否為獨占鎖,如果是,則當前state+1(可重入鎖),表示獲取鎖成功,更新state值,并返回true。這里更新state變量,不需要CAS更新,因為,當前線程已經獲得鎖。

    private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}enq(node);return node;}

    將構造的同步節點加入到同步隊列中
    1. 使用鏈表的方式把該Node節點添加到隊列尾部,如果tail的前驅節點不為空(隊列不為空),則進行CAS添加到隊列尾部。
    2. 如果更新失敗(存在并發競爭更新),則進入enq方法進行添加

    private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}

    該方法使用CAS自旋的方式來保證向隊列中添加Node(同步節點簡寫Node)
    1. 如果隊列為空,則把當前Node設置成頭節點
    2. 如果隊列不為空,則向隊列尾部添加Node

    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)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

    在acquireQueued方法中,當前線程在死循環中嘗試獲取同步狀態,
    1. 如果當前節點的前驅節點頭節點才能嘗試獲得鎖,如果獲得成功,則把當前線程設置成頭結點,把之前的頭結點從隊列中移除,等待垃圾回收(沒有對象引用)
    2. 如果獲取鎖失敗則進入shouldParkAfterFailedAcquire方法中檢測當前節點是否可以被安全的掛起(阻塞),如果可以安全掛起則進入parkAndCheckInterrupt方法,把當前線程掛起,并檢查剛線程是否執行了interrupted方法。

    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);pred.next = node;} else {/** 嘗試將當前節點的前驅節點的等待狀態設為SIGNAL* 1/這為什么用CAS,現在已經入隊成功了,前驅節點就是pred,除了node外應該沒有別的線程在操作這個節點了,那為什么還要用CAS?而不直接賦值呢?* (解釋:因為pred可以自己將自己的狀態改為cancel,也就是pred的狀態可能同時會有兩條線程(pred和node)去操作)* 2/既然前驅節點已經設為SIGNAL了,為什么最后還要返回false* (因為CAS可能會失敗,這里不管失敗與否,都返回false,下一次執行該方法的之后,pred的等待狀態就是SIGNAL了)* (網上摘抄的,解釋的很明白)*/compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}

    waitStatus狀態值

    狀態值說明
    CANCELLED1等待超時或者中斷,需要從同步隊列中取消
    SIGNAL-1后繼節點出于等待狀態,當前節點釋放鎖后將會喚醒后繼節點
    CONDITION-2節點在等待隊列中,節點線程等待在Condition上,其它線程對Condition調用signal()方法后,該節點將會從等待同步隊列中移到同步隊列中,然后等待獲取鎖。
    PROPAGATE-3表示下一次共享式同步狀態獲取將會無條件地傳播下去
    INITIAL0初始狀態
  • 首先獲取前驅節點的等待狀態ws
  • 如果ws為SIGNAL則表示可以被前驅節點喚醒,當前線程就可以掛起,等待前驅節點喚醒,返回true(可以掛起)
  • 如果ws>0說明,前驅節點取消了,并循環查找此前驅節點之前所有連續取消的節點。并返回false(不能掛起)。
  • 嘗試將當前節點的前驅節點的等待狀態設為SIGNAL
  • private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}

    把當前線程掛起,并檢查剛線程是否執行了interrupted方法,并返回true、false。

    公平鎖

    公平鎖和非公平鎖實現方式是一樣的,唯一不同的是tryAcquire方法的實現,下面是公平鎖tryAcquire方法實現:

    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;}
  • 首先獲取當前鎖狀態,如果當前state==0(無鎖),則進行獲取鎖操作
  • hasQueuedPredecessors方法判斷頭結點是否當前線程,如果是當前線程則進行CAS更新獲得鎖,獲取成功,把當前線程設置成獨占鎖。
  • 如果不是頭結點或獲取鎖失敗則,則判斷當前線程是否為獨占鎖,如果是,則當前state+1(可重入鎖),表示獲取鎖成功,更新state值,并返回true。這里更新state變量,不需要CAS更新,因為,當前線程已經獲得鎖。

    ????本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8
    ????點擊這里快速進入簡書

  • 總結

    以上是生活随笔為你收集整理的ReentrantLock 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。