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