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)魄健。
- 在加入隊列的過程中需要保證線程安全驹针,因此同步器提供了一個基于CAS的設(shè)置尾節(jié)點(diǎn)的方法:compareAndSetTail(Node expect, Node update)
- 同步隊列遵循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)其垄。
獨(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)蔫浆。
。姐叁。瓦盛。。還有好幾個外潜,感覺應(yīng)該不會面到原环,下次再看。处窥。嘱吗。。滔驾。谒麦。。
重入鎖
表示該鎖能夠支持一個線程對資源的重復(fù)加鎖嵌灰,還支持獲取鎖時的公平和非公平性選擇弄匕。
synchronized關(guān)鍵字隱式的支持重進(jìn)入。
如果在絕對時間上沽瞭,先對鎖進(jìn)行獲取的請求一定先被滿足迁匠,那么這個鎖是公平的,反之驹溃,是不公平的城丧。公平的獲取鎖,也就是等待時間最長的線程最優(yōu)先獲取鎖豌鹤,也可以說獲取鎖是順序的亡哄。ReentrantLock提供了一個構(gòu)造函數(shù),能夠控制鎖是否是公平的布疙。
-
重進(jìn)入是指任意線程在獲取到鎖之后能夠再次獲取該鎖而不會被鎖所阻塞蚊惯,該特性的實(shí)現(xiàn)需要解決以下兩個問題愿卸。
- 線程再次獲取鎖。鎖需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程截型,如果是趴荸,則再次成功獲取。
- 鎖的最終釋放宦焦。線程重復(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)的劃分方式:
寫鎖
- 寫鎖是一個支持重進(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提供的阻塞和喚醒方法
Condition接口
獲取一個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)從尾部加入等待隊列。如下所示: