【Java核心基礎(chǔ)知識(shí)】07 - 多線程并發(fā)(6)

多線程知識(shí)點(diǎn)目錄

多線程并發(fā)(1)- http://www.reibang.com/p/8fcfcac74033
多線程并發(fā)(2)-http://www.reibang.com/p/a0c5095ad103
多線程并發(fā)(3)-http://www.reibang.com/p/c5c3bbd42c35
多線程并發(fā)(4)-http://www.reibang.com/p/e45807a9853e
多線程并發(fā)(5)-http://www.reibang.com/p/5217588d82ba
多線程并發(fā)(6)-http://www.reibang.com/p/d7c888a9c03c

十九、CAS(比較并交換-樂(lè)觀鎖機(jī)制-鎖自旋)

19.1 概念及特性

CAS(Compare And Swap/Set)比較并交換童社。
CAS算法的過(guò)程:包含3個(gè)參數(shù)CAS(V,E,N)匈辱,分別為:

  • V表示要更新的變量(內(nèi)存值)
  • E表示預(yù)期值(舊的)
  • N表示當(dāng)前值(新的)

當(dāng)且僅當(dāng)V值等于E值時(shí)胜蛉,才會(huì)將V的值設(shè)為N,如果V值和E值不同表窘,則說(shuō)明已經(jīng)有其他線程做了更新匣屡,則當(dāng)前線程什么都不做,最后女气,CAS返回當(dāng)前V的真實(shí)值杏慰。
CAS操作是抱著樂(lè)觀的態(tài)度進(jìn)行的(樂(lè)觀鎖),它總是認(rèn)為自己可以成功完成操作。當(dāng)多個(gè)線程同時(shí)使用CAS操作一個(gè)變量時(shí)缘滥,只有一個(gè)會(huì)勝出轰胁,并成功更新,其余均會(huì)失敗朝扼。失敗的線程不會(huì)被掛起赃阀,僅被告知失敗,并且允許再次嘗試擎颖,當(dāng)然也允許失敗的線程放棄操作榛斯。基于這樣的原理搂捧,CAS操作即使沒(méi)有鎖驮俗,也可以發(fā)現(xiàn)其他線程對(duì)當(dāng)前線程的干擾,并進(jìn)行恰當(dāng)?shù)奶幚怼?/p>

19.2 原子包 java.util.concurrent.atomic(鎖自旋)

JDK1.5的原子包:java.util.concurrent.atomic這個(gè)包里面提供了一組原子類允跑。其基本的特性就是在多線程環(huán)境下王凑,當(dāng)有多個(gè)線程同時(shí)執(zhí)行這些類的實(shí)力包含的方法時(shí),具有排他性聋丝,即當(dāng)某個(gè)線程進(jìn)入方法索烹,執(zhí)行其中的指令時(shí),不會(huì)被其他現(xiàn)成打斷弱睦,而別的現(xiàn)成就像自旋鎖一樣术荤,一直等到該方法執(zhí)行完成,才由JVM從等待隊(duì)列中選擇另一個(gè)線程進(jìn)入每篷,這只是一種邏輯上的理解。

相對(duì)于synchronized這種阻塞算法端圈,CAS是非阻塞算法的一種常見(jiàn)實(shí)現(xiàn)(它在多線程環(huán)境中無(wú)需使用阻塞鎖焦读,從而減少了線程的阻塞和上下文切換,進(jìn)而提高了程序的性能舱权。)矗晃。由于一般CPU切換時(shí)間比CPU指令集操作更長(zhǎng),所以J.U.C在性能上有了很大的提升宴倍。

19.3 ABA問(wèn)題

CAS的ABA問(wèn)題是指在并發(fā)編程中张症,使用CAS算法時(shí)可能會(huì)出現(xiàn)的一種問(wèn)題。

ABA問(wèn)題的原因是鸵贬,CAS算法需要在操作值的時(shí)候俗他,檢查某地址的內(nèi)容有沒(méi)有發(fā)生變化(和舊值進(jìn)行比較),如果沒(méi)有發(fā)生變化則更新為新的值阔逼。但是兆衅,如果一個(gè)值原來(lái)是A,變成了B,又變成了A羡亩,那么使用CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)它的值沒(méi)有發(fā)生變化摩疑,但是實(shí)際上卻變化了。即:這個(gè)線程在操作的時(shí)候畏铆,其他線程也在進(jìn)行操作雷袋。

例如,有兩個(gè)線程T1和T2辞居,T1從內(nèi)存地址X中取出A楷怒,這時(shí)另一個(gè)線程T2也從內(nèi)存地址X中取出A,并且線程T2進(jìn)行了一系列操作將值改變成B速侈,寫(xiě)回主物理內(nèi)存率寡。然后線程T2又將內(nèi)存地址為X的數(shù)據(jù)變?yōu)锳,這個(gè)時(shí)候線程T1進(jìn)行了CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A倚搬,這時(shí)線程T1進(jìn)行CAS操作成功冶共。盡管線程T1的CAS操作成功,但并不代表這個(gè)過(guò)程就沒(méi)有問(wèn)題每界,這就是ABA問(wèn)題捅僵。

為了解決ABA問(wèn)題,可以使用版本號(hào)眨层。那么A→B→A就會(huì)變成1A→2B→3A庙楚。具體來(lái)說(shuō),可以在每個(gè)值前面加上一個(gè)版本號(hào)趴樱,每次更新時(shí)將版本號(hào)加一馒闷。當(dāng)進(jìn)行CAS操作時(shí),除了比較值之外叁征,還需要比較版本號(hào)纳账。只有當(dāng)版本號(hào)不變時(shí),才說(shuō)明值沒(méi)有發(fā)生變化捺疼。這種方法可以確保在并發(fā)環(huán)境下正確地更新值疏虫。

二十、AQS(抽象的隊(duì)列同步器)

20.1 概念及特性

AbstractQueuedSynchronized類如其名啤呼,抽象的隊(duì)列式的同步器卧秘,AQS定義了一套多線程訪問(wèn)共享資源的同步器框架,許多同步類實(shí)現(xiàn)都依賴于它官扣,如常用的ReenTrantLock翅敌、Semaphore、CountDownLatch醇锚。

AbstractQueuedSynchronized底層結(jié)構(gòu)

它維護(hù)了一個(gè)volatile int state(代表共享資源)和一個(gè)FIFO線程等待隊(duì)列(多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列)哼御。

這里的 volatile 是關(guān)鍵字坯临,它確保了 state 的可見(jiàn)性。具體來(lái)說(shuō)恋昼,volatile 關(guān)鍵字可以確保每個(gè)線程在讀取 state 時(shí)都能得到最新的值看靠,而不是讀取到已經(jīng)被其他線程修改過(guò)的舊值。

state 的訪問(wèn)方式主要有三種:getState()液肌、setState() 和 compareAndSetState():

  1. getState():這個(gè)方法用于獲取當(dāng)前的狀態(tài)值挟炬。
  2. setState():這個(gè)方法用于設(shè)置新的狀態(tài)值。
  3. compareAndSetState():這個(gè)方法比較當(dāng)前狀態(tài)值和期望的狀態(tài)值嗦哆,如果兩者相等谤祖,則設(shè)置新的狀態(tài)值。這是一種原子操作老速,可以確保在多線程環(huán)境下粥喜,狀態(tài)的改變是原子的。

當(dāng)一個(gè)線程需要獲取資源時(shí)橘券,它首先會(huì)調(diào)用 getState() 方法來(lái)查看當(dāng)前的狀態(tài)额湘。如果狀態(tài)指示資源可用,那么線程就可以獲取資源并進(jìn)行操作旁舰。如果狀態(tài)指示資源不可用锋华,那么線程就會(huì)被放入等待隊(duì)列中,直到狀態(tài)變?yōu)榭捎谩?/p>

當(dāng)線程釋放資源時(shí)箭窜,它會(huì)調(diào)用 setState() 方法來(lái)改變狀態(tài)值毯焕。如果此時(shí)沒(méi)有其他線程在等待該資源,那么狀態(tài)改變后磺樱,等待隊(duì)列中的線程就可以按順序獲取資源并進(jìn)行操作纳猫。如果有其他線程在等待該資源找颓,那么狀態(tài)改變后屯伞,等待隊(duì)列中的線程將再次被放入隊(duì)列中等待。

20.2 AQS定義兩種資源共享方式

Exclusive 獨(dú)占資源(ReenTrantLock)

只有一個(gè)線程能執(zhí)行傻盟,如 ReentrantLock活孩。它又可分為公平鎖和非公平鎖:

  • 公平鎖:按照線程在隊(duì)列中的排隊(duì)順序,先到者先拿到鎖乖仇。
  • 非公平鎖:當(dāng)線程要獲取鎖時(shí)憾儒,無(wú)視隊(duì)列順序直接去搶鎖,誰(shuí)搶到就是誰(shuí)的乃沙。

資源共享和獲取/釋放的接口:tryAcquire() 和 tryRelease() 方法起趾。當(dāng)一個(gè)線程嘗試獲取資源時(shí),如果資源當(dāng)前不可用警儒,那么該線程會(huì)被放入等待隊(duì)列训裆,直到資源可用眶根。當(dāng)線程釋放資源時(shí),會(huì)檢查是否有其他線程在等待該資源边琉。

Share共享資源(Semaphore/CountDownLatch)

多個(gè)線程可同時(shí)執(zhí)行属百,如 CountDownLatch、Semaphore变姨、CyclicBarrier族扰、ReadWriteLock。例如定欧,ReentrantReadWriteLock 可以看成是組合式渔呵,因?yàn)?ReentrantReadWriteLock 也就是讀寫(xiě)鎖允許多個(gè)線程同時(shí)對(duì)某一資源進(jìn)行讀。

資源共享和獲取/釋放的接口:tryAcquireShared() 和 tryReleaseShared() 方法砍鸠。在共享模式下扩氢,多個(gè)線程可以同時(shí)獲取資源,但通常會(huì)有一個(gè)限制爷辱,比如同時(shí)最多有幾個(gè)線程可以獲取資源录豺。當(dāng)一個(gè)線程嘗試獲取資源時(shí),如果資源當(dāng)前不可用托嚣,那么該線程會(huì)被放入等待隊(duì)列巩检,直到資源可用。當(dāng)線程釋放資源時(shí)示启,會(huì)喚醒等待隊(duì)列中的一個(gè)線程兢哭。


由于獨(dú)占模式和共享模式的接口方法不同,因此 AQS 本身并沒(méi)有定義成抽象類夫嗓,而是定義了一個(gè)接口迟螺。這樣可以允許實(shí)現(xiàn)類根據(jù)具體的需求來(lái)實(shí)現(xiàn)不同的接口方法。

對(duì)于自定義同步器來(lái)說(shuō)舍咖,只需要實(shí)現(xiàn) state 的獲取和釋放方式即可矩父。具體的線程等待隊(duì)列的維護(hù)(如獲取資源失敗時(shí)的入隊(duì)操作、喚醒出隊(duì)等)已經(jīng)由 AQS 在頂層實(shí)現(xiàn)了排霉。這樣可以讓自定義同步器專注于實(shí)現(xiàn)具體的資源獲取和釋放策略窍株,而不需要關(guān)心線程等待隊(duì)列的維護(hù)細(xì)節(jié)。

20.3 同步器的實(shí)現(xiàn)

ReentrantLock為例攻柠,它的實(shí)現(xiàn)確實(shí)是基于 AQS(AbstractQueuedSynchronized)的球订,state 變量用來(lái)表示資源的狀態(tài)。在 ReentrantLock 的 lock()方法中瑰钮,會(huì)調(diào)用 tryAcquire()方法嘗試獲取資源冒滩,如果 state 為 0因苹,表示資源未被占用扶檐,這時(shí)線程會(huì)將 state 加 1蘸秘,并設(shè)置自己的狀態(tài)為擁有資源狀態(tài)醋虏。此后哮翘,其他線程再嘗試 lock()時(shí)就會(huì)失敗,直到當(dāng)前擁有資源的線程調(diào)用 unlock()方法釋放資源阻课,將 state 設(shè)置為 0,其他線程才有機(jī)會(huì)獲取該鎖署驻。

CountDownLatch的例子中,state 變量也用來(lái)表示資源的狀態(tài)宣吱,但它的作用是記錄需要等待的子線程數(shù)量。在 CountDownLatch 的構(gòu)造函數(shù)中瞳别,會(huì)將 state 初始化為 N,表示有 N 個(gè)子線程需要執(zhí)行痪宰。然后扮饶,這 N 個(gè)子線程會(huì)并行執(zhí)行任務(wù)甜无,每個(gè)子線程執(zhí)行完后會(huì)調(diào)用 countDown()方法將 state 減 1奥帘。當(dāng) state 減為 0 時(shí)仪召,表示所有子線程都已執(zhí)行完畢已旧,這時(shí)就會(huì)喚醒主調(diào)用線程吐句,使其從 await()方法返回文虏,繼續(xù)后續(xù)操作殖演。

無(wú)論是 ReentrantLock 還是 CountDownLatch氧秘,它們都利用了 AQS 提供的隊(duì)列和 state 變量等核心機(jī)制來(lái)實(shí)現(xiàn)資源的獲取和釋放。通過(guò)這種方式趴久,可以將同步器的實(shí)現(xiàn)與具體的資源獲取和釋放策略分離丸相,從而提供更大的靈活性。

20.4 ReentrantReadWriteLock 實(shí)現(xiàn)獨(dú)占和共享兩種方式

ReentrantReadWriteLock 是 Java 中的一個(gè)讀寫(xiě)鎖彼棍,它允許多個(gè)線程同時(shí)讀取共享資源灭忠,但只允許一個(gè)線程寫(xiě)入共享資源膳算。在 ReentrantReadWriteLock 中,讀鎖和寫(xiě)鎖是互斥的弛作,因此 ReentrantReadWriteLock 實(shí)現(xiàn)的是獨(dú)占式的資源訪問(wèn)涕蜂。

在 ReentrantReadWriteLock 中,讀鎖和寫(xiě)鎖的獲取和釋放都是通過(guò)內(nèi)部的鎖來(lái)實(shí)現(xiàn)的映琳。讀鎖和寫(xiě)鎖的狀態(tài)都是通過(guò) state 變量來(lái)維護(hù)的机隙。當(dāng)一個(gè)線程獲取寫(xiě)鎖時(shí),它會(huì)先將 state 加 1萨西,表示有一個(gè)寫(xiě)線程正在等待獲取鎖有鹿。如果此時(shí)還有讀線程正在等待獲取讀鎖,那么這些讀線程可以繼續(xù)等待獲取鎖原杂。但是印颤,如果此時(shí)沒(méi)有讀線程正在等待獲取讀鎖,那么獲取寫(xiě)鎖的線程就可以獲取到鎖并執(zhí)行穿肄。

在實(shí)現(xiàn) ReentrantReadWriteLock 時(shí)年局,需要同時(shí)實(shí)現(xiàn)獨(dú)占和共享兩種方式。具體來(lái)說(shuō)咸产,需要實(shí)現(xiàn) tryAcquire()和 tryRelease()方法來(lái)支持獨(dú)占式的資源訪問(wèn)矢否,同時(shí)還需要實(shí)現(xiàn) tryAcquireShared()和 tryReleaseShared()方法來(lái)支持共享式的資源訪問(wèn)。在 ReentrantReadWriteLock 中脑溢,獲取讀鎖和寫(xiě)鎖的過(guò)程是原子性的僵朗,因此可以實(shí)現(xiàn)精確的控制。

總之屑彻,ReentrantReadWriteLock 是一種支持獨(dú)占和共享兩種方式的同步器验庙,它通過(guò)內(nèi)部的鎖來(lái)實(shí)現(xiàn)精確的控制,允許多個(gè)線程同時(shí)讀取共享資源社牲,但只允許一個(gè)線程寫(xiě)入共享資源粪薛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搏恤,隨后出現(xiàn)的幾起案子违寿,更是在濱河造成了極大的恐慌,老刑警劉巖熟空,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藤巢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡息罗,警方通過(guò)查閱死者的電腦和手機(jī)掂咒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人绍刮,你說(shuō)我怎么就攤上這事糜工。” “怎么了录淡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)油坝。 經(jīng)常有香客問(wèn)我嫉戚,道長(zhǎng),這世上最難降的妖魔是什么澈圈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任彬檀,我火速辦了婚禮,結(jié)果婚禮上瞬女,老公的妹妹穿的比我還像新娘窍帝。我一直安慰自己,他們只是感情好诽偷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布坤学。 她就那樣靜靜地躺著,像睡著了一般报慕。 火紅的嫁衣襯著肌膚如雪深浮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天眠冈,我揣著相機(jī)與錄音飞苇,去河邊找鬼。 笑死蜗顽,一個(gè)胖子當(dāng)著我的面吹牛布卡,可吹牛的內(nèi)容都是我干的雇盖。 我是一名探鬼主播这弧,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼卷哩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冷溶!你這毒婦竟也來(lái)了纯衍?” 一聲冷哼從身側(cè)響起襟诸,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陷揪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體玷坠,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妄田。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情综慎,我是刑警寧澤涣仿,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站示惊,受9級(jí)特大地震影響好港,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜米罚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一钧汹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧录择,春花似錦拔莱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至动看,卻和暖如春尊剔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菱皆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工须误, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仇轻。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓京痢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拯田。 傳聞我的和親對(duì)象是個(gè)殘疾皇子历造,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容