Lock框架是jdk1.5新增的钮莲,作用和synchronized的作用一樣,所以學習的時候可以和synchronized做對比倒淫。在這里先和synchronized做一下簡單對比座菠,然后分析下Lock接口以及ReentrantLock的源碼和說明姜钳。具體的其他的Lock實現(xiàn)的分析在后面會慢慢介紹。
Lock框架和synchronized
有關(guān)synchronized的作用和用法不在具體說明领迈,應(yīng)該都很熟悉了彻磁。而Lock有著和synchronized一樣的語意,但是比synchronized多了一些功能狸捅,單單就從Lock接口定義來看衷蜓,比synchronized多出來的功能有:
- 可中斷的獲取鎖,就是獲取鎖的線程可以響應(yīng)中斷尘喝。
- 可以嘗試獲取鎖磁浇,也就是非阻塞獲取鎖,一個線程可以嘗試去獲取鎖朽褪,獲取成功就持有鎖并返回true置吓,否則返回false。
- 帶超時的嘗試獲取鎖缔赠,就是在嘗試獲取鎖的時候交洗,會有超時時間,當?shù)竭_了指定的時間后橡淑,還未獲取到鎖构拳,就返回false。
除了定義之外梁棠,Lock框架還和synchronized有不一樣的是:
- Lock需要顯示的加鎖和釋放鎖置森,而且一定要在finally中去釋放鎖。而synchronized則不需要我們?nèi)リP(guān)心鎖的釋放符糊。
- 鎖的公平性凫海,Lock接口并沒有定義有關(guān)公平性的方法,而是在具體的實現(xiàn)類中使用AQS來實現(xiàn)鎖的公平性男娄。
Lock接口源碼
public interface Lock {
//獲取鎖行贪,獲取到鎖后返回
//注意一定要記得釋放鎖
void lock();
//可中斷的獲取鎖
//獲取鎖的時候漾稀,如果線程正在等待獲取鎖,則該線程能響應(yīng)中斷
void lockInterruptibly() throws InterruptedException;
//嘗試獲取鎖建瘫,當線程獲取鎖的時候崭捍,獲取成功與否都會立即返回
//不會一直等著去獲取鎖
boolean tryLock();
//帶有超時時間的嘗試獲取鎖
//在一定的時間內(nèi)獲取到鎖會返回true
//在這段時間內(nèi)被中斷了,會返回
//在這段時間內(nèi)啰脚,沒有獲取到鎖殷蛇,會返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//釋放鎖
void unlock();
//獲取一個Condition對象。
Condition newCondition();
}
Lock接口的定義并不復(fù)雜橄浓,獲取鎖釋放鎖以及非阻塞式的獲取鎖等方法的定義粒梦。其實想象一下日常使用的時候,也大概是如此荸实,獲取鎖匀们,釋放鎖,獲取鎖的時候沒有得到鎖准给,我就轉(zhuǎn)一圈回來再試試昼蛀,等到一定時間之后,我就不要了圆存,走了叼旋。接口的定義沒有太多要說的,接下來看Lock接口的實現(xiàn)沦辙。
Lock接口的實現(xiàn)
Lock接口主要的實現(xiàn)是ReentrantLock重入鎖夫植,另外還有ConcurrentHashMap中的Segment繼承了ReentrantLock,在ReentrantReadWriteLock中的WriteLock和ReadLock也實現(xiàn)了Lock接口油讯。
ReentrantLock
ReentrantLock是一個可重入的互斥鎖详民,等同于synchronized,但是比synchronized更強大靈活陌兑,減少死鎖發(fā)生的概率沈跨。我們上面說Lock框架提供了公平鎖的機制,就是在ReentrantLock中有提供公平鎖機制的實現(xiàn)兔综,默認為非公平鎖饿凛。
在繼續(xù)看ReentrantLock的各個方法實現(xiàn)之前,首先需要了解下內(nèi)部是怎么實現(xiàn)公平鎖和非公平鎖的软驰,其實想一下也簡單涧窒,比如我就是一可重入鎖ReentrantLock,你想從我這獲得到公平的還是不公平的锭亏,但是不能我說什么就是什么纠吴,我這里有一個天平(AQS),這個是大家公認的可以實現(xiàn)公平和不公平的機器慧瘤,你找我要戴已,我就給天平說一聲固该,他來操作,然后我再把結(jié)果給你糖儡。(越描述越復(fù)雜7セ怠)。幾乎ReentrantLock中所有的操作都會交給Sync去實現(xiàn)休玩。
有關(guān)AQS這里不做介紹著淆,在AQS專門的文章有介紹劫狠,請自行查閱把拴疤。接下來就看看我拿著的兩把鎖,公平和不公平独泞。
Sync
Sync是公平和非公平兩種的基類呐矾,直接看代碼,看不明白的話懦砂,可以先看下面公平和非公平的解析蜒犯,然后再返回來:
//繼承自AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
//由具體的子類實現(xiàn),即公平和非公平的有不同實現(xiàn)
abstract void lock();
//非公平的嘗試獲取
final boolean nonfairTryAcquire(int acquires) {
//當前線程
final Thread current = Thread.currentThread();
//當前AQS同步器的狀態(tài)
int c = getState();
//狀態(tài)為0荞膘,說明沒有人獲取鎖
if (c == 0) {
//嘗試獲取罚随,獲取成功設(shè)為獨占模式
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//這里解釋跟公平的一樣,參照下面的
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//嘗試釋放
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//是否是獨占
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
//獲取持有者線程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//獲取重入數(shù)
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//是否鎖了
final boolean isLocked() {
return getState() != 0;
}
//
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
FairSync
公平鎖的實現(xiàn)羽资,有關(guān)公平的實現(xiàn)淘菩,是此類進行處理的。
//繼承自Sync
static final class FairSync extends Sync {
//獲取鎖
//公平的lock方法交給了AQS的acquire方法去處理
//acquire方法采用獨占模式屠升,并且忽略中斷
//而AQS獲取鎖的實現(xiàn)是先使用tryAcquire方法獲取潮改,獲取不到就加入到隊列中,一直嘗試獲取腹暖,直到成功返回汇在,
//tryAcquire的實現(xiàn)又是具體的子類實現(xiàn)的,下面的tryAcquire方法就是公平的tryAcquire實現(xiàn)
//
final void lock() {
//參數(shù)1是AQS的同步狀態(tài)
//首先了解下AQS中同步狀態(tài)的定義脏答,state
//0表示未被獲取到鎖糕殉,1表示已經(jīng)被獲取到鎖了,大于1表示重入數(shù)
//我們要獲取鎖殖告,肯定是想要現(xiàn)在的同步狀態(tài)為0糙麦,然后我們把狀態(tài)變成1,這樣鎖就是我們的了
acquire(1);
}
//公平的tryAcquire方法實現(xiàn)
protected final boolean tryAcquire(int acquires) {
//當前線程
final Thread current = Thread.currentThread();
//獲取AQS的同步狀態(tài)
int c = getState();
//狀態(tài)為0的話丛肮,說明沒有其他人獲取到鎖
if (c == 0) {
//hasQueuedPredecessors查詢是否還有其他線程比當前線程等待獲取鎖的時間更長
//compareAndSetState使用cas來設(shè)置狀態(tài)赡磅,預(yù)期為0,我們想要設(shè)置的值為1
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//如果我們是等待獲取時間最長的(這就是公平宝与,我等的時間長焚廊,就該我第一個被服務(wù))
//并且cas設(shè)置成功了冶匹,表示我們獲取到鎖了
//設(shè)置當前線程為獨占訪問
setExclusiveOwnerThread(current);
return true;
}
}
//往下是state不為0的情況,也就是1咆瘟,或者大于1
//如果當前線程和獨占線程是同一個
else if (current == getExclusiveOwnerThread()) {
//當前狀態(tài)加上我們要獲取的參數(shù)1
//現(xiàn)在表示的是重入數(shù)
int nextc = c + acquires;
//state是一個32位整型嚼隘,小于0,表示重入數(shù)超過了最大數(shù)
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//設(shè)置當前狀態(tài)
setState(nextc);
return true;
}
return false;
}
}
可以看到公平的tryAcquire在獲取鎖的開始只調(diào)用一次袒餐,獲取到就獲取到了飞蛹,或者已經(jīng)獲取到增加重入數(shù),沒有獲取到就返回false灸眼,如果返回false的話卧檐,AQS就會將其加入到隊列中一直嘗試獲取。
NonfairSync
//也是繼承自Sync
static final class NonfairSync extends Sync {
//非公平的獲取鎖
final void lock() {
//先嘗試直接獲取鎖
//如果能獲取到鎖焰宣,就設(shè)置為獨占模式
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//直接獲取不到的話霉囚,就會跟公平鎖一樣的流程去獲取
//tryAcquire在下面
acquire(1);
}
//這里是非公平的tryAcquire,直接調(diào)用Sync中的nofairTryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平和非公平的區(qū)別
上面看完了代碼匕积,有點模糊盈罐,感覺代碼都差不多,到底公平和非公平差別在哪里闪唆。首先看下公平的鎖獲取盅粪,公平鎖獲取會直接調(diào)用acquire方法,acquire方法并不是直接去獲取鎖悄蕾,而是調(diào)用公平的tryAcquire方法票顾,公平的tryAcquire方法首先獲取到當前同步器狀態(tài),如果沒有人用同步器笼吟,也就是狀態(tài)為0库物,會先去判斷有沒有人比我等的時間更長,有的話我就不能獲取鎖贷帮,而是讓別人先去戚揭;如果我是等待最長的那個,我就去使用CAS更改狀態(tài)撵枢,獲取鎖民晒。
而非公平的實現(xiàn)是,我上來就直接使用CAS獲取鎖锄禽,不問別人是不是等著很長時間了潜必,我獲取到了就是我的了,我獲取不到沃但,再調(diào)用acquire方法磁滚,然后acquire方法中調(diào)用非公平的tryAcquire方法,非公平的tryAcquire方法也是很直接,如果當前鎖沒有人用垂攘,也就是state為0维雇,我不管有沒有人比我等的時間長,我就去獲取晒他,然后設(shè)置獨占吱型。
公平鎖,獲取鎖首先去嘗試陨仅,沒有的話就排隊津滞,輪到我之后,還要去問一下有沒有等的時間更長的灼伤。非公平鎖則是不排隊触徐,直接上,沒有獲取到饺蔑,我也嘗試獲取锌介,嘗試獲取的時候我還是直接上嗜诀,不管其他人猾警。
了解完公平和非公平鎖,再去看其他方法就沒那么難了隆敢。
ReentrantLock構(gòu)造函數(shù)
//可以看到发皿,默認是非公平的
public ReentrantLock() {
sync = new NonfairSync();
}
還可以指定公平性
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
lock方法
//直接調(diào)用公平或者非公平的同步器的lock方法
public void lock() {
sync.lock();
}
lock方法有三種情況:
- 如果鎖沒有被其他線程持有,當前線程立即獲得鎖并返回拂蝎,同步器狀態(tài)設(shè)為1穴墅。
- 如果當前線程已經(jīng)持有鎖,則狀態(tài)加1温自,并立即返回玄货。
- 如果鎖被其他線程持有,當前線程掛起直到獲取到鎖悼泌,然后返回松捉,同步器設(shè)為1。
lockInterruptibly方法
//可中斷的獲取鎖
public void lockInterruptibly() throws InterruptedException {
//調(diào)用AQS的方法
sync.acquireInterruptibly(1);
}
獲取鎖馆里,可以被Thread.interrupt打斷隘世。也有三種情況:
如果鎖沒有被其他線程持有,當前線程立即獲得鎖并返回鸠踪,同步器狀態(tài)設(shè)為1丙者。
如果當前線程已經(jīng)持有鎖,則狀態(tài)加1营密,并立即返回械媒。
-
如果鎖被其他線程持有,當前線程會掛起去獲取鎖评汰,在這個過程會有兩種情況:
- 當前線程獲取到了鎖纷捞,返回侣集,同步器狀態(tài)設(shè)置1。
- 當前線程被中斷了兰绣,會拋出InterruptedException異常世分,并清除中斷狀態(tài)。
tryLock方法
//嘗試獲取鎖缀辩,不會阻塞臭埋,成功與否都會直接返回
public boolean tryLock() {
//使用的是非公平鎖的獲取
return sync.nonfairTryAcquire(1);
}
使用非公平的鎖獲取,如果使用了公平的臀玄,獲取的時候還要判斷別人是不是了好久瓢阴,而非公平的nonfairTryAcquire,能獲取就直接獲取到健无,獲取不到就返回false荣恐,比較直接。
如果不想破壞公平性累贤,可以使用帶有超時時間的tryLock方法叠穆。
帶超時的tryLock方法
//在超時間內(nèi),并且沒有被打斷臼膏,鎖沒有被其他線程持有硼被,就立即獲取到鎖并返回
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
如果使用的是公平鎖,如果有其他等的時間更長的線程渗磅,即便現(xiàn)在鎖沒有人持有嚷硫,當前線程也不會獲取到鎖,給等的時間更長的去獲取始鱼。
unlock方法
//釋放鎖
//直接調(diào)用AQS來釋放鎖
public void unlock() {
sync.release(1);
}
newCondition方法
//返回一個新的Condition實例
public Condition newCondition() {
return sync.newCondition();
}
返回的Condition實例的方法仔掸,其實和Object的wait,notify医清,notifyAll方法的作用一樣起暮。
其他方法
//重入次數(shù)
public int getHoldCount() {
return sync.getHoldCount();
}
//鎖是否被當前線程持有
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//查詢鎖是否被任何線程持有
public boolean isLocked() {
return sync.isLocked();
}
//是否是公平鎖
public final boolean isFair() {
return sync instanceof FairSync;
}
//返回當前擁有鎖的線程
protected Thread getOwner() {
return sync.getOwner();
}
//查詢是否有線程在排隊獲取鎖
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
//查詢給定的線程是否在等待獲取鎖
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
//得到正在等待獲取鎖的隊列的長度
public final int getQueueLength() {
return sync.getQueueLength();
}
//獲取正在等待獲取鎖的所有線程
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
//查詢是否有線程正在等待給定的Condition
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//得到正在等待一個Condition的隊列的長度
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//獲取所有的等待某個Condition的線程集合
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
ReentrantLock和synchronized的區(qū)別
ReentrantLock和synchronized很類似,但是比synchronized多了更多功能状勤,比如可中斷鎖鞋怀,鎖可以帶超時時間,可以嘗試非阻塞獲取鎖等持搜。ReentrantLock還提供了條件Condition密似,跟Object的wait/notify類似,但是在多個條件變量和高度競爭鎖的地方葫盼,ReentrantLock更加合適残腌。
另外AQS是重點,一定要多學幾遍,學會了抛猫,才能掌握鎖(我還不太明白s№铩)。
有關(guān)其他實現(xiàn)闺金,比如ReentrantReadWriteLock的ReadLock和WriteLock以及ConcurrentHashMap中的Segment會另行介紹逾滥。