摘要:接著線程過來通過方式獲取鎖,獲取鎖的過程就是通過操作變量將其值從變為。線程加鎖成功后還有一步重要的操作,就是將設置成為自己。線程屁顛屁顛的就去等待區小憩一會去了。
一、寫在前面
這篇文章,我們聊一聊Java并發中的核武器, AQS底層實現。
不管是工作三四年、還是五六年的在工作或者面試中涉及到并發的是時候總是繞不過AQS這個詞。
首先,確實還有很多人連AQS是什么都不知道,甚至有的竟不知其為何物。或者有的聽說過其名,但怎么拼寫的都忘記了。
總的來說確實有很多同學對AQS總有一種云里霧里的感覺,在搜索引擎中搜下AQS看個幾篇文章,估計對其還是醉醺醺的。
所以根據上面的難點,這篇我們使用由簡入難的方式,讓你一次搞定這Java并發中這個核武器AQS。
二、ReentrantLock 和 AQS 的關系首先我們以你最受的方式帶你進入這個核武器庫,Java 并發包下的 ReentrantLock大家肯定很熟悉了。
基本上學過Java 的都知道ReentrantLock,下面我就不多說了直接上一段代碼。
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖 // 業務邏輯代碼 } finally { lock.unlock(); // 釋放鎖 }
這段代碼大家應該很熟悉了,無非就是獲取一把鎖,加鎖和釋放鎖的過程。
有同學就問了這和AQS有毛關系呀!別著急,告訴你關系大著去了。在Java并發包中很多鎖都是通過AQS來實現加鎖和釋放鎖的過程的,AQS就是并發包基礎。
例如:ReentrantLock、ReentrantReadWriteLock 底層都是通過AQS來實現的。
那么AQS到底為何物尼?別急,我們一步一來揭開其神秘的面紗。
AQS 的全稱 AbstractQueuedSynchronizers抽象隊列同步器,給大家畫三張圖來說明其在Java 并發包的地位、 長啥樣、和ReentrantLock 的關系。
通過此類圖可以彰顯出了AQS的地位、上層鎖實現基本都是通過其底層來實現的。
有沒有被忽悠的感覺?你沒看錯AQS就長這個鳥樣。說白了其內部就是包含了三個組件
state 資源狀態
exclusiveOwnerThread 持有資源的線程
CLH 同步等待隊列。
在看這張圖現在明白ReentrantLock 和 AQS 的關系了吧!大白話說就是ReentrantLock其內部包含一個AQS對象(內部類),AQS就是ReentrantLock可以獲取和釋放鎖實現的核心部件。
好了! 經過上面的介紹估計大家已經對AQS混了個臉熟,下面我們就來說說這一段代碼。
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖 // 業務邏輯代碼 } finally { lock.unlock(); // 釋放鎖 }
這段代碼加鎖和釋放鎖到底會發生什么故事尼?
很簡單在AQS 內部有一個核心變量 (volatile)state 變量其代表了加鎖的狀態,初始值為0。
另外一個重要的關鍵 OwnerThread 持有鎖的線程,默認值為null 在回顧下這張圖。
接著線程1過來通過lock.lock()方式獲取鎖,獲取鎖的過程就是通過CAS操作volatile 變量state 將其值從0變為1。
如果之前沒有人獲取鎖,那么state的值肯定為0,此時線程1加鎖成功將state = 1。
線程1加鎖成功后還有一步重要的操作,就是將OwnerThread 設置成為自己。如下圖線程1加鎖過程。
其實到這大家應該對AQS有個大概認識了,說白了就是并發包下面的一個核心組件,其內部維持state變量、線程變量等核型的東西,來實現加鎖和釋放鎖的過程。
大家有沒有不管是ReentrantLock還是ReentrantReadWriteLock 等為什么都是Reentrant 開頭尼?
從單詞本身意思也能看出,Reentrant 可重入的意思 ,也就說其是一個可重入鎖。
可重入鎖?
就是你可以對一個 ReentrantLock 進行多次的lock() 和 unlock() 操作,也就是可以對一個鎖加多次,叫做可重入鎖。 來一段代碼直觀感受下。
ReentrantLock lock = new ReentrantLock(); try { lock.lock(); // 加鎖1 // 業務邏輯代碼 lock.lock() // 加鎖2 // 業務邏輯代碼 lock.lock() // 加鎖3 } finally { lock.unlock(); // 釋放鎖3 lock.unlock(); // 釋放鎖2 lock.unlock(); // 釋放鎖1 }
注意:釋放鎖是由內到外依次釋放的,不可缺少。
問題又來了?ReentrantLock 內部又是如何來實現的尼?
說白了!還是我們AQS這個核心組件幫我實現的,很 easy~ 上述兩個核心變量 state 和 OwnerThread 還記得吧!
重入就是判斷當前鎖是不是自己加上的,如果是就代表自己可以在次上鎖,每重入一次就是將state值加1。就是這么簡單啦!!!
說完了可重入我們再來看看鎖的互斥又是如何實現的尼?
此時線程2也跑過來想加鎖,CAS操作嘗試將 state 從0 變成 1, 哎呀!糟糕state已經不是0了,說明此鎖已經被別人拿到了。
接著線程2想??? 這個鎖是不是我以前加上的,瞅瞅 OwnerThread=線程1 哎! 明顯不是自己上的 ,悲催加鎖失敗了~~~。來張圖記錄下線程2的悲苦經歷。
可是線程2加鎖失敗將何去何從尼?
線程2:想,要是有個地方讓我休息下,等線程1釋放鎖后通知我下再來從新嘗試上鎖就好了。
這時我們的核心部件AQS又登場了!
AQS: OK! 好吧!那我就給你提供一個落腳地吧(CLH)進去待著吧!一會讓線程1叫你。
線程2: 屁顛屁顛的就去等待區小憩一會去了。同樣來張圖記錄下線程2高興樣。
此時線程1業務執行完了,開始釋放鎖
將state值改為0
將OwnerThread 設為null
通知線程2鎖我已經用完了,該你登場了
線程2一聽,樂壞了!立馬開始嘗試獲取取鎖,CAS 嘗試將 state 值設為 1 ,如果成功將OwnerThread設為自己 線程2。
此時線程2成功獲取到了鎖,再來張圖瞅瞅。
Ok !到這借著Reentrantkock 的加鎖和釋放鎖的過程給大家講解了一下AQS工作原理。
用一句話總結下:AQS就是Java并發包下的一個基礎組件,用來實現各種鎖和同步組件的,其核心分為三個組件。
Volatile state 變量
OwnerThread 加鎖線程
CLH 同步等待隊列
等并發核心組件。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74219.html
摘要:二什么是重入鎖可重入鎖,顧名思義,支持重新進入的鎖,其表示該鎖能支持一個線程對資源的重復加鎖。將由最近成功獲得鎖,并且還沒有釋放該鎖的線程所擁有。可以使用和方法來檢查此情況是否發生。 一、寫在前面 前幾篇我們具體的聊了AQS原理以及底層源碼的實現,具體參見 《J.U.C|一文搞懂AQS》《J.U.C|同步隊列(CLH)》《J.U.C|AQS獨占式源碼分析》《J.U.C|AQS共享式源...
摘要:公平鎖阻塞隊列前邊有線程,要去后邊排隊,簡單來說滾后邊等著去。非公平鎖不管是否有線程排隊,先槍鎖基于實現的可重入鎖實現類。 AQS原理介紹: AQS (Abstra...
摘要:二什么是同步隊列同步隊列一個雙向隊列,隊列中每個節點等待前驅節點釋放共享狀態鎖被喚醒就可以了。三入列操作如上圖了解了同步隊列的結構,我們在分析其入列操作在簡單不過。 一、寫在前面 在上篇我們聊到AQS的原理,具體參見《J.U.C|AQS原理》。 這篇我們來給大家聊聊AQS中核心同步隊列(CLH)。 二、什么是同步隊列(CLH) 同步隊列 一個FIFO雙向隊列,隊列中每個節點等待前驅...
摘要:本章我們主要聊獨占式即同一時刻只能有一個線程獲取同步狀態,其它獲取同步狀態失敗的線程則會加入到同步隊列中進行等待。到這獨占式獲取同步和釋放同步狀態的源碼已經分析完了。 一、寫在前面 上篇文章通過ReentrantLock 的加鎖和釋放鎖過程給大家聊了聊AQS架構以及實現原理,具體參見《J.U.C|AQS的原理》。 理解了原理,我們在來看看再來一步一步的聊聊其源碼是如何實現的。 本章給...
摘要:主要講解方法共享式獲取同步狀態,返回值表示獲取成功,反之則失敗。源碼分析同步器的和方法請求共享鎖的入口當并且時才去才獲取資源獲取鎖以共享不可中斷模式獲取鎖將當前線程一共享方式構建成節點并將其加入到同步隊列的尾部。 一、寫在前面 上篇給大家聊了獨占式的源碼,具體參見《J.U.C|AQS獨占式源碼分析》 這一章我們繼續在AQS的源碼世界中遨游,解讀共享式同步狀態的獲取和釋放。 二、什么是...
閱讀 2012·2021-11-24 09:39
閱讀 1882·2019-08-30 15:55
閱讀 2173·2019-08-30 15:53
閱讀 571·2019-08-29 13:16
閱讀 989·2019-08-26 12:20
閱讀 2386·2019-08-26 11:58
閱讀 3144·2019-08-26 10:19
閱讀 3305·2019-08-23 18:31