Java中的鎖

Lock接口

  • Lock接口比synchronized多的特性:
特性 描述
嘗試非阻塞地獲取鎖 當(dāng)前線程嘗試獲取鎖,如果這一時刻鎖沒有被其他線程獲取到断医,則成功獲取并持有鎖
能被中斷地獲取鎖 與synchronized不同腌歉,獲取到鎖的線程能夠響應(yīng)中斷涵妥,當(dāng)獲取到鎖的線程被中斷時,中斷異常將會被拋出代芜,同時鎖會被釋放
超時獲取鎖 在指定的截止時間之前獲取鎖策州,如果截止時間到了仍舊無法獲取鎖瘸味,則返回

隊列同步器

  • 使用一個int成員表示同步狀態(tài),通過內(nèi)置的FIFO隊列來完成資源獲取線程的排隊工作够挂。
  • 同步器的設(shè)計是基于模板方法模式的旁仿,也就是說,使用者需要繼承同步器并重寫指定的方法孽糖,隨后將同步器組合在自定義同步組件的實(shí)現(xiàn)中枯冈,并調(diào)用同步器提供的模板方法,而這些模板方法將會調(diào)用使用者重寫的方法办悟。

隊列同步器實(shí)現(xiàn)分析

同步隊列
  • 同步器依賴內(nèi)部的同步隊列(一個FIFO雙向隊列)來完成同步狀態(tài)的管理尘奏,當(dāng)前線程獲取同步狀態(tài)失敗時,同步器會將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成為一個節(jié)點(diǎn)(Node)并將其加入同步隊列病蛉,同時阻塞當(dāng)前線程炫加,當(dāng)同步狀態(tài)釋放時,會把首節(jié)點(diǎn)中的線程喚醒铺然,使其再次嘗試獲取同步狀態(tài)俗孝。
  • 同步隊列中的節(jié)點(diǎn)(Node)用來保存獲取同步狀態(tài)失敗的線程引用、等待狀態(tài)以及前驅(qū)和后繼節(jié)點(diǎn)魄健。
image.png
  • 在加入隊列的過程中需要保證線程安全驹针,因此同步器提供了一個基于CAS的設(shè)置尾節(jié)點(diǎn)的方法:compareAndSetTail(Node expect, Node update)
image.png
  • 同步隊列遵循FIFO,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn)诀艰,首節(jié)點(diǎn)的線程在釋放同步狀態(tài)時柬甥,將會喚醒后繼節(jié)點(diǎn),而后繼節(jié)點(diǎn)在獲取同步狀態(tài)成功時將自己設(shè)置為首節(jié)點(diǎn)其垄。
image.png
獨(dú)占式同步狀態(tài)獲取與釋放
  • 獲瓤疗选:首先調(diào)用自定義同步器實(shí)現(xiàn)的tryAcquire(int arg)方法,該方法保證線程安全的獲取同步狀態(tài)绿满,如果同步狀態(tài)獲取失敗臂外,則構(gòu)造同步節(jié)點(diǎn)(獨(dú)占式Node.EXCLUSIVE,同一時刻只能有一個線程成功獲取同步狀態(tài))并通過addWaiter(Node node)方法將該節(jié)點(diǎn)加入到同步隊列的尾部喇颁,最后調(diào)用acquireQueued(Node node,int arg)方法漏健,使得該節(jié)點(diǎn)以“死循環(huán)”的方式獲取同步狀態(tài)。如果獲取不到則阻塞節(jié)點(diǎn)中的線程橘霎,而被阻塞線程的喚醒主要依靠前驅(qū)節(jié)點(diǎn)的出隊或阻塞線程被中斷來實(shí)現(xiàn)蔫浆。


    image.png

。姐叁。瓦盛。。還有好幾個外潜,感覺應(yīng)該不會面到原环,下次再看。处窥。嘱吗。。滔驾。谒麦。。

重入鎖

  • 表示該鎖能夠支持一個線程對資源的重復(fù)加鎖嵌灰,還支持獲取鎖時的公平和非公平性選擇弄匕。

  • synchronized關(guān)鍵字隱式的支持重進(jìn)入。

  • 如果在絕對時間上沽瞭,先對鎖進(jìn)行獲取的請求一定先被滿足迁匠,那么這個鎖是公平的,反之驹溃,是不公平的城丧。公平的獲取鎖,也就是等待時間最長的線程最優(yōu)先獲取鎖豌鹤,也可以說獲取鎖是順序的亡哄。ReentrantLock提供了一個構(gòu)造函數(shù),能夠控制鎖是否是公平的布疙。

  • 重進(jìn)入是指任意線程在獲取到鎖之后能夠再次獲取該鎖而不會被鎖所阻塞蚊惯,該特性的實(shí)現(xiàn)需要解決以下兩個問題愿卸。

    1. 線程再次獲取鎖。鎖需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程截型,如果是趴荸,則再次成功獲取。
    2. 鎖的最終釋放宦焦。線程重復(fù)n次獲取了鎖发钝,隨后在第n次釋放該鎖后,其他線程能夠獲取到該鎖波闹。鎖的最終釋放要求鎖對于獲取進(jìn)行計數(shù)自增酝豪,計數(shù)表示當(dāng)前鎖被重復(fù)獲取的次數(shù),而鎖被釋放時精堕,計數(shù)自減孵淘,當(dāng)計數(shù)等于0時表示鎖已經(jīng)成功釋放。
  • 如果該鎖被獲取了n次锄码,那么前(n-1)次tryRelease(int release)方法必須返回false夺英,而只有同步狀態(tài)完全釋放了,才能返回true滋捶。

讀寫鎖

  • 讀寫鎖在同一時刻可以允許多個線程訪問痛悯,但是在寫線程訪問時,所有的讀線程和其他寫線程均被阻塞重窟。讀寫鎖維護(hù)了一對鎖载萌,一個讀鎖和一個寫鎖,通過分離讀鎖和寫鎖巡扇,使得并發(fā)性相比一般的排他鎖有了很大的提升扭仁。
  • 讀寫鎖的自定義同步器需要在同步狀態(tài)(一個整型變量)上維護(hù)多個讀線程和一個寫線程的狀態(tài),使得該狀態(tài)的設(shè)計成為讀寫鎖實(shí)現(xiàn)的關(guān)鍵厅翔。
  • 讀寫鎖狀態(tài)的劃分方式:
    image.png

寫鎖

  • 寫鎖是一個支持重進(jìn)入的排他鎖乖坠。如果當(dāng)前線程已經(jīng)獲取了寫鎖,則增加寫狀態(tài)刀闷。如果當(dāng)前線程在獲取寫鎖時熊泵,讀鎖已經(jīng)被獲取(讀狀態(tài)不為0)或者該線程不是已經(jīng)獲取寫鎖的線程甸昏,則當(dāng)前線程進(jìn)入等待狀態(tài)顽分。
  • 如果存在讀鎖,則寫鎖不能被獲取施蜜,原因在于:讀寫鎖要確保寫鎖的操作對讀鎖可見卒蘸,如果允許讀鎖在已經(jīng)被獲取的情況下對寫鎖的獲取,那么正在運(yùn)行的其他讀線程就無法感知到當(dāng)前寫線程的操作翻默。
  • 寫鎖每次釋放時均減少寫狀態(tài)缸沃,當(dāng)寫狀態(tài)為0時表示寫鎖已被釋放恰起,從而等待的讀線程能夠繼續(xù)訪問讀寫鎖,同時前次線程的修改對后續(xù)讀寫線程可見和泌。

讀鎖

  • 讀鎖是一個支持重進(jìn)入的共享鎖村缸,它能夠被多個線程同時獲取,在沒有其他寫線程訪問(或者寫狀態(tài)為0)時武氓,讀鎖總會被成功地獲取,而所做的也只是(線程安全的)增加讀狀態(tài)仇箱。如果當(dāng)前線程已經(jīng)獲取了讀鎖县恕,則增加讀狀態(tài)。如果當(dāng)前線程在獲取讀鎖時剂桥,寫鎖已被其他線程獲取忠烛,則進(jìn)入等待狀態(tài)。

鎖降級

  • 鎖降級指的是寫鎖降級成為讀鎖权逗。鎖降級是指把持酌朗(當(dāng)前擁有的)寫鎖,再獲取到讀鎖斟薇,隨后釋放(先前擁有的)寫鎖的過程师坎。
  • 鎖降級中讀鎖的獲取是否必要呢?答案是必要的堪滨。主要是為了保證數(shù)據(jù)的可見性胯陋,如果當(dāng)前線程不獲取讀鎖而是直接釋放寫鎖,假設(shè)此刻另一個線程(記作線程T)獲取了寫鎖并修改了數(shù)據(jù)袱箱,那么當(dāng)前線程無法感知線程T的數(shù)據(jù)更新遏乔。如果當(dāng)前線程獲取讀鎖,即遵循鎖降級的步驟发笔,則線程T將會被阻塞盟萨,直到當(dāng)前線程使用數(shù)據(jù)并釋放讀鎖之后,線程T才能獲取寫鎖進(jìn)行數(shù)據(jù)更新了讨。
  • RentrantReadWriteLock不支持鎖升級(把持讀鎖捻激、獲取寫鎖,最后釋放讀鎖的過程)量蕊。目的也是保證數(shù)據(jù)可見性铺罢,如果讀鎖已被多個線程獲取,其中任意線程成功獲取了寫鎖并更新了數(shù)據(jù)残炮,則其更新對其他獲取到讀鎖的線程是不可見的韭赘。

LockSupport工具

  • LockSupport提供的阻塞和喚醒方法
  • image.png

Condition接口

  • image.png
  • 獲取一個Condition必須通過Lock的newCondition()方法

  • 等待隊列是一個FIFO的隊列,在隊列中的每個節(jié)點(diǎn)都包含了一個線程引用势就,該線程就是在Condition對象上等待的線程泉瞻,如果一個線程調(diào)用了Condition.await()方法脉漏,那么該線程將會釋放鎖、構(gòu)造成節(jié)點(diǎn)加入等待隊列并進(jìn)入等待狀態(tài)袖牙。

  • 一個Condition包含一個等待隊列侧巨,Condition擁有首節(jié)點(diǎn)(firstWaiter)和尾節(jié)點(diǎn)(lastWaiter)。當(dāng)前線程調(diào)用Condition.await()方法鞭达,將會以當(dāng)前線程構(gòu)造節(jié)點(diǎn)司忱,并將節(jié)點(diǎn)從尾部加入等待隊列。如下所示:
    image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畴蹭,一起剝皮案震驚了整個濱河市坦仍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叨襟,老刑警劉巖繁扎,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糊闽,居然都是意外死亡梳玫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門右犹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來提澎,“玉大人,你說我怎么就攤上這事傀履∈洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵钓账,是天一觀的道長碴犬。 經(jīng)常有香客問我,道長梆暮,這世上最難降的妖魔是什么服协? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮啦粹,結(jié)果婚禮上偿荷,老公的妹妹穿的比我還像新娘。我一直安慰自己唠椭,他們只是感情好跳纳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贪嫂,像睡著了一般寺庄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天斗塘,我揣著相機(jī)與錄音赢织,去河邊找鬼。 笑死馍盟,一個胖子當(dāng)著我的面吹牛于置,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贞岭,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼八毯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞄桨?” 一聲冷哼從身側(cè)響起宪彩,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎讲婚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俊柔,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筹麸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雏婶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片物赶。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖留晚,靈堂內(nèi)的尸體忽然破棺而出酵紫,到底是詐尸還是另有隱情,我是刑警寧澤错维,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布奖地,位于F島的核電站,受9級特大地震影響赋焕,放射性物質(zhì)發(fā)生泄漏参歹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一隆判、第九天 我趴在偏房一處隱蔽的房頂上張望犬庇。 院中可真熱鬧,春花似錦侨嘀、人聲如沸臭挽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欢峰。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赤赊,已是汗流浹背闯狱。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抛计,地道東北人哄孤。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像吹截,于是被迫代替她去往敵國和親瘦陈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

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