摘要:概述前面已經講解了關于的非公平鎖模式,關于非公平鎖,內部其實告訴我們誰先爭搶到鎖誰就先獲得資源,下面就來分析一下公平鎖內部是如何實現公平的如果沒有看過非公平鎖的先去了解下非公平鎖,因為這篇文章前面不會講太多內部結構,直接會對源碼進行分析前文
概述
前面已經講解了關于AQS的非公平鎖模式,關于NonfairSync非公平鎖,內部其實告訴我們誰先爭搶到鎖誰就先獲得資源,下面就來分析一下公平鎖FairSync內部是如何實現公平的?如果沒有看過非公平鎖的先去了解下非公平鎖,因為這篇文章前面不會講太多內部結構,直接會對源碼進行分析
前文連接地址:圖解AQS原理之ReentrantLock詳解-非公平鎖
本文分析的JDK版本是1.8源碼分析溫馨提示:讀本文內容建議結合之前寫的非公平,前篇設計了很多基礎性內容
在源碼分析之前,我們先來看一下ReentrantLock如何切換獲取鎖的模式呢?其實是在構造器中傳遞指定的類型變量來控制使用鎖的方式,如下所示:
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
當fair參數指定為true時,代表的是公平鎖,如果指定為false則使用的非公平,無參的構造函數默認使用的是非公平模式,如下所示:
public ReentrantLock() { sync = new NonfairSync(); }
接下來我們以一個例子來進行后面的說明:
public class ReentrantLockDemo { public static void main(String[] args) throws Exception { AddDemo runnalbeDemo = new AddDemo(); Thread thread = new Thread(runnalbeDemo::add); thread.start(); Thread.sleep(500); Thread thread1 = new Thread(runnalbeDemo::add); thread1.start(); System.out.println(runnalbeDemo.getCount()); } private static class AddDemo { private final AtomicInteger count = new AtomicInteger(); private final ReentrantLock reentrantLock = new ReentrantLock(true); private final Condition condition = reentrantLock.newCondition(); private void add() { try { reentrantLock.lockInterruptibly(); count.getAndIncrement(); } catch (Exception ex) { System.out.println("線程被中斷了"); } finally { // reentrantLock.unlock(); } } int getCount() { return count.get(); } } }
我們通過源碼可以看到這里我們啟動了兩個線程,兩個線程分別進行同步鎖操作,這里我并沒有釋放掉鎖,因為方便分析隊列的情況,當然你也可以在內部寫一個死循環,不釋放鎖就可以了,我這里簡單的不釋放鎖,使用的是可中斷的獲取鎖操作方法lockInterruptibly,這里內部的原理我們上一篇文章中已經講解過了,這里并不過多的去分析內部原理,這個ReentrantLock的lockInterruptibly調用內部類AQS的acquireInterruptibly,但是其實是FairSync內部類繼承了內部類Sync,而內部類Sync有繼承了AbstractQueuedSynchronizer簡稱AQS,acquireInterruptibly源碼信息如下所示:
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
這里我們通過上一篇文章得知tryAcquire是需要子類去實現的方法,我們在例子中指定了使用的是公平鎖,所以tryAcquire方法的實現是在ReentrentLock的FairSync類中,我們來具體看一下這個方法,重點也在這個方法中其他的其實都是一樣的,因為用的方法都會一樣的非公平和公平鎖的調用,唯獨不一樣的就是子類實現的方法是不相同的,接下來我們就來看一下公平鎖的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; //state遞增 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); //設置state狀態 return true; //獲得鎖成功 } return false; //獲得鎖失敗 }
對比非公平鎖的NonfairSync類的tryAcquire方法,其實就是在鎖可用的情況下增加了一個判斷條件,這個判斷方法就是hasQueuedPredecessors,從方法的名稱來看說的是有等待的線程隊列,換句話說已經有人在排隊了,新來的線程你就不能加塞,而非公平模式的誰先爭搶到鎖就是誰的,管你先來不先來,接下來我們具體看一下這個
hasQueuedPredecessors方法源碼:
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; // 獲得尾節點 Node h = head; // 獲得頭節點 Node s; return h != t && //頭節點和尾節點相同代表隊列為空 ((s = h.next) == null || s.thread != Thread.currentThread()); //頭節點的next節點為空代表頭節點,以及s.thread不是當前線程不是自己的話代表隊列中存在元素 }
通過上面的源碼信息,可以得出其實內部主要就是判斷有沒有排隊等待的節點,隊列是否為空,如果為空的話則可以爭搶鎖,如果隊列不為空,伙計你必須老老實實給我排隊去,除非占有鎖的線程和請求鎖的線程是一樣的,否則還是老老實實排隊去,這就是公平模式的鎖操作,還有一個lock方法,公平模式的lock方法,沒有直接上來先獲取鎖,而是先嘗試獲得鎖直接調用AQS的aquire方法進行嘗試獲取鎖,下面是FairSync源碼:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); //這里直接調用了aquire并沒有嘗試修改state狀態 } /** * Fair version of tryAcquire. Don"t grant access unless * recursive call or no waiters or is first. */ 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; } }結束語
本內容主要是結合上篇內容的一個續篇,可以結合上篇然后再看下篇會比較清晰些。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74749.html
摘要:內部提供了兩種的實現,一種公平模式,一種是非公平模式,如果沒有特別指定在構造器中,默認是非公平的模式,我們可以看一下無參的構造函數。 概述 并發編程中,ReentrantLock的使用是比較多的,包括之前講的LinkedBlockingQueue和ArrayBlockQueue的內部都是使用的ReentrantLock,談到它又不能的不說AQS,AQS的全稱是AbstractQueue...
摘要:所以大家看下面的圖,就是線程跑過來加鎖的一個過程。好線程現在就重新嘗試加鎖,這時還是用操作將從變為,此時就會成功,成功之后代表加鎖成功,就會將設置為。此外,還要把加鎖線程設置為線程自己,同時線程自己就從等待隊列中出隊了。 ReentrantLock和AQS的關系 ReentrantLock使用 showImg(https://segmentfault.com/img/bVbkZr4?...
摘要:的主要功能和關鍵字一致,均是用于多線程的同步。而僅支持通過查詢當前線程是否持有鎖。由于和使用的是同一把可重入鎖,所以線程可以進入方法,并再次獲得鎖,而不會被阻塞住。公平與非公平公平與非公平指的是線程獲取鎖的方式。 1.簡介 可重入鎖ReentrantLock自 JDK 1.5 被引入,功能上與synchronized關鍵字類似。所謂的可重入是指,線程可對同一把鎖進行重復加鎖,而不會被阻...
摘要:開始獲取鎖終于輪到出場了,的調用過程和完全一樣,同樣拿不到鎖,然后加入到等待隊列隊尾然后,在阻塞前需要把前驅結點的狀態置為,以確保將來可以被喚醒至此,的執行也暫告一段落了安心得在等待隊列中睡覺。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首發于一世流云的專欄:https://segmentfault...
摘要:公平策略在多個線程爭用鎖的情況下,公平策略傾向于將訪問權授予等待時間最長的線程。使用方式的典型調用方式如下二類原理的源碼非常簡單,它通過內部類實現了框架,接口的實現僅僅是對的的簡單封裝,參見原理多線程進階七鎖框架獨占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首發于一世流云的專欄:https...
閱讀 3917·2021-09-09 09:33
閱讀 1773·2021-09-06 15:14
閱讀 1919·2019-08-30 15:44
閱讀 3074·2019-08-29 18:36
閱讀 3765·2019-08-29 16:22
閱讀 2095·2019-08-29 16:21
閱讀 2530·2019-08-29 15:42
閱讀 1648·2019-08-29 11:00