章節(jié)目錄
- Lock接口與Synchronized的區(qū)別及特性
- 隊(duì)列同步器的接口與自定義鎖示例
- 隊(duì)列同步器的實(shí)現(xiàn)分析
1.Lock接口與Synchronized的區(qū)別及特性
特性 | 描述 |
---|---|
嘗試非阻塞性的獲取鎖 | 當(dāng)前線程嘗試獲取鎖(自旋獲取鎖)烙无,如果這一時(shí)刻鎖沒(méi)有被其他線程獲取到情妖,則成功獲取并持有鎖 |
能被中斷的獲取鎖 | 已獲取鎖的線程可以響應(yīng)中斷兵怯,當(dāng)獲取到鎖的線程被中斷時(shí),可以拋出中斷異常功偿,同時(shí)鎖會(huì)被釋放 |
超時(shí)獲取鎖 | 在指定的截止時(shí)間之前獲取鎖或渤,如果截止時(shí)間到了仍然沒(méi)有獲取到鎖朋魔,則返回 |
注意:Lock接口的實(shí)現(xiàn)基本上都是通過(guò)聚合了一個(gè)同步器的子類來(lái)完成線程訪問(wèn)控制的
隊(duì)里同步器的接口與定義鎖示例
隊(duì)列同步器定義:
隊(duì)列同步器函荣,是用來(lái)構(gòu)建鎖與其它同步組件的基礎(chǔ)框架,基本數(shù)據(jù)結(jié)構(gòu)與內(nèi)容是:
1浸踩、int state -> state 標(biāo)示同步狀態(tài)叔汁;
2、內(nèi)置的FIFO來(lái)完成獲取同步狀態(tài)的線程的排隊(duì)工作检碗。
隊(duì)列同步器使用方式
1据块、子類通過(guò)繼承同步器并實(shí)現(xiàn)它的抽象方法來(lái)管理同步狀態(tài);
2折剃、實(shí)現(xiàn)過(guò)程中對(duì)同步狀態(tài)的更改另假,通過(guò)
setState()、
setState(int newState)怕犁、
compareAndSetState(int expect,int newUpdateValue)
來(lái)進(jìn)行操作边篮,保證狀態(tài)改變時(shí)原子性的己莺、安全的;
3戈轿、實(shí)現(xiàn)同步器的子類被推薦為自定義同步組件的靜態(tài)內(nèi)部類凌受;
4、同步器可以支持獨(dú)占式的獲取同步狀態(tài)(ReentrantLock)思杯、也可以支持共享
式的獲取同步狀態(tài)(ReentrantReadWriteLock)
對(duì)于同步器與鎖的關(guān)系可以這樣理解:
- 在鎖的實(shí)現(xiàn)中聚合同步器胜蛉,利用同步器實(shí)現(xiàn)鎖的語(yǔ)義。
- 鎖面向使用者色乾,它定義了使用者與鎖的交互接口誊册,隱藏了實(shí)現(xiàn)細(xì)節(jié)。
- 同步器面向的是鎖的實(shí)現(xiàn)者暖璧,它簡(jiǎn)化了鎖的實(shí)現(xiàn)方式案怯,屏蔽了同步器狀態(tài)管理、線程排隊(duì)澎办、等待與喚醒等底層操作殴泰。
2.隊(duì)列同步器的接口與自定義鎖示例
2.1 模板方法模式
同步器的設(shè)置是基于**模版方法模式**,使用者需要繼承同步器并重寫指定的方
法,隨后將同步器組合在自定義同步組件的實(shí)現(xiàn)中浮驳,并調(diào)用同步器提供的模板
方法,而這些模板方法將會(huì)調(diào)用使用者重寫的方法捞魁。
2.2 重寫同步器指定的方法
getState():獲取當(dāng)前同步狀態(tài)
setState(int newState):設(shè)置當(dāng)前同步狀態(tài)
compareAndSetState(int expect,int update): 使用CAS設(shè)置當(dāng)前的狀態(tài)至会,該方
法保證狀態(tài)設(shè)置的原子性
2.3 同步器可重寫的方法
方法名稱 | 描述 |
---|---|
protected boolean tryAcquire(int arg) | 獨(dú)占式獲取同步狀態(tài),實(shí)現(xiàn)該方法需要查詢當(dāng)前狀態(tài)并判斷同步狀態(tài)是否符合預(yù)期谱俭,然后再進(jìn)行CAS設(shè)置同步狀態(tài) |
protected boolean tryRelease(int arg) | 獨(dú)占式釋放同步狀態(tài)奉件,等待獲取同步狀態(tài)的線程將有機(jī)會(huì)獲取同步狀態(tài)(公平性獲取鎖) |
protected int tryAcquireShared(int arg) | 共享式獲取同步狀態(tài),返回>=0的值昆著,標(biāo)示獲取成功县貌,反之獲取失敗 |
protected boolean tryReleaseShared(int arg) | 共享式釋放同步狀態(tài) |
protected boolean isHeldExclusively() | 當(dāng)前同步器是否在獨(dú)占模式下被線程占用,一般該方法表示是否被當(dāng)前線程所獨(dú)占 |
2.4 獨(dú)占鎖示例
package org.seckill.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 利用了模板方法模式
*/
public class Mutex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer {
//是否處于占用狀態(tài)
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
//當(dāng)狀態(tài)為0時(shí)獲取鎖
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//釋放鎖凑懂,將當(dāng)前狀態(tài)設(shè)置為0
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
//返回一個(gè)condition,每個(gè)condition中都包含了一個(gè)condition隊(duì)列
Condition newCondition() {
return new ConditionObject();
}
}
//僅需要將操作代理到Sync上即可
private Sync sync = new Sync();
public void lock() {
sync.acquire(1);//調(diào)用tryAccquire
}
//當(dāng)前已獲取鎖的線程響應(yīng)中斷煤痕,釋放鎖,拋出異常接谨,并返回
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);//嘗試立即獲取鎖
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));//嘗試超時(shí)獲取鎖
}
public void unlock() {
sync.release(1);//釋放鎖
}
public Condition newCondition() {
return sync.newCondition();
}
}
總結(jié)-實(shí)現(xiàn)同步組件的方法
1. 獨(dú)占鎖Mutex 是一個(gè)自定義同步組件摆碉,它允許同一時(shí)刻只允許同一個(gè)線程占有鎖。
2.Mutex中定義了一個(gè)私有靜態(tài)內(nèi)部類脓豪,該類繼承了同步器并實(shí)現(xiàn)了獨(dú)占式獲取和釋放同步狀態(tài)巷帝。
3.在tryAcquire(int acquires)方法中,經(jīng)過(guò)CAS設(shè)置成功(同步狀態(tài)設(shè)置為1)扫夜,則
代表獲取了同步狀態(tài)楞泼,而在tryRelease(int releases) 方法中只是將同步狀態(tài)重
置為0驰徊。
3 隊(duì)列同步器的實(shí)現(xiàn)分析
3.1 同步隊(duì)列數(shù)據(jù)結(jié)構(gòu)
- 同步器依賴內(nèi)部的同步隊(duì)列,即一個(gè)FIFO的隊(duì)列堕阔,這個(gè)隊(duì)列由雙向鏈表實(shí)現(xiàn)棍厂。節(jié)點(diǎn)數(shù)據(jù)從 隊(duì)列尾部插入,頭部刪除印蔬。
- node 數(shù)據(jù)結(jié)構(gòu)
struct node {
node prev; //節(jié)點(diǎn)前驅(qū)節(jié)點(diǎn)
node next; //節(jié)點(diǎn)后繼節(jié)點(diǎn)
Thread thread; //獲取同步狀態(tài)的線程
int waitStatus; //等待狀態(tài)
Node nextWaiter; //等待隊(duì)列中的后繼節(jié)點(diǎn)
}
等待隊(duì)列 后續(xù)篇章介紹到condition會(huì)有相關(guān)記錄勋桶。
同步隊(duì)列基本結(jié)構(gòu)
3.2 無(wú)法獲取到同步狀態(tài)的線程節(jié)點(diǎn)被加入到同步隊(duì)列的尾部
本質(zhì)上是采用 compareAndSetTail(Node expect,Node update),當(dāng)一個(gè)線程成功的獲取了同步狀態(tài)
(或者鎖)侥猬,其他線程將無(wú)法獲取到同步狀態(tài)例驹,轉(zhuǎn)而被構(gòu)造成為節(jié)點(diǎn)并加入到同步隊(duì)列中,而這個(gè)加入隊(duì)列的過(guò)程
必須要保證線程安全退唠。所以采用了基于CAS的方式來(lái)設(shè)置尾節(jié)點(diǎn)的方法鹃锈。
,需要傳遞當(dāng)前節(jié)點(diǎn)認(rèn)為的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)瞧预,只有設(shè)置成功后屎债,當(dāng)前節(jié)點(diǎn)才正式與之前的尾節(jié)點(diǎn)建立關(guān)聯(lián)。
3.3 成功獲取同步狀態(tài)
同步隊(duì)列遵循FIFO垢油,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn)盆驹,首節(jié)點(diǎn)的線程在釋放
同步狀態(tài)時(shí),會(huì)喚醒后繼節(jié)點(diǎn)滩愁,而后繼節(jié)點(diǎn)將會(huì)在獲取同步狀態(tài)成功時(shí)躯喇,將自己設(shè)置為首節(jié)點(diǎn)。
3.4 獨(dú)占式同步狀態(tài)獲取與釋放
- 前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且能夠獲取同步狀態(tài)的判斷條件和線程進(jìn)入同步隊(duì)列 來(lái)獲
取同步狀態(tài)是自旋的過(guò)程硝枉。 - 設(shè)置首節(jié)點(diǎn)是通過(guò)獲取同步狀態(tài)成功的線程來(lái)完成的acquireQueued(node,args)完成的
獨(dú)占式獲取同步狀態(tài)的流程圖
獨(dú)占式同步狀態(tài)(鎖)獲取流程