看這部分的前提是大家已經(jīng)看過(guò)AbstractQueuedSynchronizer這個(gè)類尺棋,知道它是個(gè)啥了哈及塘,如果不知道莽使,請(qǐng)先看這里http://www.reibang.com/p/c629aebcd251锐极。
咳咳笙僚,這個(gè)鎖嘛,就是當(dāng)程序的某個(gè)部分被多個(gè)線程執(zhí)行時(shí)想讓線程挨個(gè)執(zhí)行它而不是大家一擁而上灵再,所以就用鎖來(lái)控制它肋层,這個(gè)鎖同時(shí)只能被一個(gè)線程占據(jù),這樣某些程序才不會(huì)因?yàn)椴l(fā)而出錯(cuò)翎迁。
先看怎么使用栋猖,再看實(shí)現(xiàn)原理吧。
public classReentrantTestextendsThread {
privateReentrantLockreentrantLock;
??? publicReentrantTest(ReentrantLock reentrantLock){
this.reentrantLock= reentrantLock;
???}
@Override
???public voidrun() {
//??????? reentrantLock.lock();
???????try{
for(intj =0;j <100000;j++) {
System.out.print(j);
???????????????System.out.print("\n");
???????????}
}finally{
//??????????? reentrantLock.unlock();
???????}
??? }
public static voidmain(String[] args) {
ReentrantLock reentrantLock =newReentrantLock();
???????ReentrantTest reentrantTest =newReentrantTest(reentrantLock);
???????ReentrantTest reentrantTest1 =newReentrantTest(reentrantLock);
???????reentrantTest.start();
???????reentrantTest1.start();
???}
}
這是一段簡(jiǎn)單的示例代碼汪榔,目的是為了讓每個(gè)線程可以完整打出0到99999蒲拉,不被中斷,有人會(huì)問(wèn)那你干嘛用多線程…..=痴腌。=我承認(rèn)我例子想得不好雌团,反正就這樣!大家試著跑一跑就能看到效果啦士聪,如果注釋掉上面那兩行锦援,程序輸出的結(jié)果就是混亂的,兩個(gè)線程輸出的結(jié)果是交雜的剥悟,但是用了lock就會(huì)是一個(gè)線程執(zhí)行完才下個(gè)線程灵寺。
這里又有人問(wèn)了,那這個(gè)跟在方法前加個(gè)synchronise有啥區(qū)別区岗?
首先略板,這都JDK10都要出來(lái)了,synchronise在進(jìn)行了一系列優(yōu)化后慈缔,性能還算OK叮称,跟ReentrantLock差距不大了。既然性能差的不多,那就從功能上來(lái)對(duì)比颅拦,確實(shí)synchronise很方便蒂誉,一切由jvm控制,但是它的粒度不如ReentrantLock細(xì)距帅,怎么說(shuō)呢右锨,ReentrantLock是要我們自己寫,從哪里lock碌秸,所以就算在一個(gè)方法內(nèi)部绍移,它也可以控制方法的一部分是受到鎖的控制,而另一部分不受讥电,所以粒度比較細(xì)蹂窖,但要注意一定要在finally中釋放鎖,不然程序執(zhí)行出現(xiàn)異常等提前退出的現(xiàn)象可能會(huì)導(dǎo)致死鎖恩敌。
好瞬测,然后呢,ReentrantLock提供了一些額外的功能纠炮,比如可重入月趟,可中斷,可限時(shí)恢口,可公平等孝宗,下面在講它實(shí)現(xiàn)的時(shí)候會(huì)挨個(gè)講,這些都是synchronise不具備的啦耕肩。
ReentrantLock名字叫Reentrant對(duì)吧因妇,重新進(jìn)入的意思,相信大家聽(tīng)說(shuō)過(guò)可重入鎖猿诸,這其實(shí)跟這個(gè)ReentrantLock的實(shí)現(xiàn)機(jī)制有關(guān)婚被,它內(nèi)部定義了一個(gè)私有類Sync(這個(gè)就是實(shí)現(xiàn)鎖這個(gè)功能的關(guān)鍵):
abstractstaticclassSyncextendsAbstractQueuedSynchronizer {
//序列號(hào)
privatestaticfinallongserialVersionUID = -5179523762034025860L;
//獲取鎖
abstractvoidlock();
//非公平方式獲取
finalbooleannonfairTryAcquire(intacquires) {
//當(dāng)前線程
finalThread current =Thread.currentThread();
//獲取狀態(tài)
intc =getState();
if(c == 0) {//表示沒(méi)有線程正在競(jìng)爭(zhēng)該鎖
if(compareAndSetState(0, acquires)) {//比較并設(shè)置狀態(tài)成功,狀態(tài)0表示鎖沒(méi)有被占用
//設(shè)置當(dāng)前線程獨(dú)占setExclusiveOwnerThread(current);
returntrue;//成功}
}
elseif(current == getExclusiveOwnerThread()) {//當(dāng)前線程擁有該鎖
intnextc = c + acquires;//增加重入次數(shù)
if(nextc < 0)//overflow
thrownewError("Maximum lock count exceeded");
//設(shè)置狀態(tài)setState(nextc);
//成功
returntrue;
}
//失敗
returnfalse;
}
//試圖在共享模式下獲取對(duì)象狀態(tài)两芳,此方法應(yīng)該查詢是否允許它在共享模式下獲取對(duì)象狀態(tài)摔寨,如果允許,則獲取它
protectedfinalbooleantryRelease(intreleases) {
intc = getState() -releases;
if(Thread.currentThread() != getExclusiveOwnerThread())//當(dāng)前線程不為獨(dú)占線程
thrownewIllegalMonitorStateException();//拋出異常
//釋放標(biāo)識(shí)
booleanfree =false;
if(c == 0) {
free=true;
//已經(jīng)釋放怖辆,清空獨(dú)占
setExclusiveOwnerThread(null);
}
//設(shè)置標(biāo)識(shí)setState(c);
returnfree;
}
//判斷資源是否被當(dāng)前線程占有
protectedfinalbooleanisHeldExclusively() {
//While we must in general read state before owner,
//we don't need to do so to check if current thread is owner
returngetExclusiveOwnerThread() ==Thread.currentThread();
}
//新生一個(gè)條件
finalConditionObject newCondition() {
returnnewConditionObject();
}
//Methods relayed from outer class
//返回資源的占用線程
finalThread getOwner() {
returngetState() == 0 ?null: getExclusiveOwnerThread();
}
//返回狀態(tài)
finalintgetHoldCount() {
returnisHeldExclusively() ? getState() : 0;
}
//資源是否被占用
finalbooleanisLocked() {
returngetState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
//自定義反序列化邏輯
privatevoidreadObject(java.io.ObjectInputStream s)
throwsjava.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0);//reset to unlocked state}
}
好的是复,貼了一段代碼,就是Sync的定義竖螃,注意到淑廊,它繼承自AbstractQueuedSynchronizer,我講這個(gè)是為了講ReentrantLock的實(shí)現(xiàn)機(jī)制哈特咆,你打開(kāi)源碼就知道這個(gè)ReentrantLock其實(shí)沒(méi)別的東西季惩,主要就是靠這個(gè)Sync录粱,所以得把這個(gè)Sync是干嘛的講通。好的繼續(xù)画拾,這個(gè)Sync里就有幾個(gè)起主要作用的方法啥繁,下面一一說(shuō)明這個(gè)東西是怎么工作的。
首先青抛,最重要的方法:lock():
abstract void lock();
第一部分?FairSync
它是一個(gè)抽象方法旗闽,具體的實(shí)現(xiàn)在它FairSync和NonfairSync兩個(gè)子類中(一個(gè)是公平鎖一個(gè)是不公平鎖
),當(dāng)這個(gè)方法被調(diào)用時(shí)蜜另,意味著執(zhí)行該方法的當(dāng)前線程是在試圖去獲取這個(gè)鎖适室,我們先看NonfairSync的lock方法:
final voidlock() {
???if(compareAndSetState(0,1))
??????? setExclusiveOwnerThread(Thread.currentThread());
??? else
???????acquire(1);
}
可以看到compareAndSetState(0, 1)如果此刻鎖的狀態(tài)是0就代表了鎖現(xiàn)在沒(méi)人占,那么我們當(dāng)前線程直接占了就行举瑰,否則捣辆,執(zhí)行acquire方法:
public final voidacquire(intarg) {
???if(!tryAcquire(arg) &&
??????? acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
???????selfInterrupt();
}
這個(gè)方法在AbstractQueuedSynchronizer里面哈,讓你們?nèi)タ戳舜搜福驗(yàn)樗腔A(chǔ)汽畴,然后這個(gè)方法里呢,tryAcquire則是在NonfairSync里實(shí)現(xiàn)的:
protected final booleantryAcquire(intacquires) {
???returnnonfairTryAcquire(acquires);
}
原來(lái)它也就調(diào)用的父類中的nonfairTryAcquire方法:
final booleannonfairTryAcquire(intacquires) {
???finalThread current = Thread.currentThread();
??? intc = getState();
??? if(c ==0) {
???????if(compareAndSetState(0,acquires)) {
??????????? setExclusiveOwnerThread(current);
??????????? return true;
???????}
??? }
???else if(current == getExclusiveOwnerThread()) {
???????intnextc = c + acquires;
??????? if(nextc <0)// overflow
???????????throw newError("Maximum lock count exceeded");
???????setState(nextc);
??????? return true;
???}
???return false;
}
分析一波邮屁,這個(gè)參數(shù)代表可以重復(fù)的獲取這個(gè)鎖整袁,State這個(gè)變量,存儲(chǔ)的就是這個(gè)鎖被占有的狀態(tài)佑吝,0就是沒(méi)被占有,1就是被占有一次绳匀,n就是被占有n次(這個(gè)次數(shù)可能描述不是特別恰當(dāng)芋忿,自己體會(huì)~),然后加入狀態(tài)是0疾棵,那么把這個(gè)state設(shè)置為acquires這么大戈钢,然后占有這個(gè)鎖,假如狀態(tài)不是0是尔,那么又會(huì)去判斷殉了,占有這個(gè)鎖的是不是當(dāng)前線程(不是多此一舉哈,因?yàn)榍懊嬲f(shuō)了拟枚,ReentrantLock可以重復(fù)申請(qǐng)薪铜,也就是可重入,那么就有可能這個(gè)鎖已經(jīng)被當(dāng)前線程占有啦)恩溅,那就把狀態(tài)的值再設(shè)為當(dāng)前的狀態(tài)值加上acquires隔箍,如果不滿足以上條件,啥都不干脚乡,返回false(返回false后接下來(lái)的操作就會(huì)回到AbstractQueuedSynchronizer里面去蜒滩,也就是排隊(duì)等待的那套流程,你們要看!8┘琛)
第二部分 NonFairSync
FairSync里面的lock方法更簡(jiǎn)單:
final voidlock() {
??? acquire(1);
}
直接執(zhí)行acquire方法捡遍,我們上面寫出來(lái)這個(gè)方法,所以還是會(huì)調(diào)用tryAcquire方法:
???protected final booleantryAcquire(intacquires) {
???????finalThread current = Thread.currentThread();
??????? intc = getState();
??????? if(c ==0) {
???????????if(!hasQueuedPredecessors() &&
??????????????? compareAndSetState(0,acquires)) {
??????????????? setExclusiveOwnerThread(current);
??????????????? return true;
???????????}
??????? }
???????else if(current == getExclusiveOwnerThread()) {
???????????intnextc = c + acquires;
??????????? if(nextc <0)
???????????????throw newError("Maximum lock count exceeded");
???????????setState(nextc);
??????????? return true;
???????}
???????return false;
???}
}
就是當(dāng)鎖狀態(tài)為0時(shí)竹握,公平鎖會(huì)調(diào)用hasQueuedPredecessors判斷是否有其他線程比它等待更長(zhǎng)時(shí)間稽莉,方法如下:? ?
public final booleanhasQueuedPredecessors() {
???Node t =tail;?
???Node h =head;
???Node s;
??? returnh != t &&
??????? ((s = h.next) ==null|| s.thread!= Thread.currentThread());
}
來(lái)看下它的判斷條件,?滿足下面幾個(gè)條件返回true:
首先等待隊(duì)列的頭節(jié)點(diǎn)不等于尾節(jié)點(diǎn),其次涩搓,頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為空污秆,或者當(dāng)前線程不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。
如果返回true代表當(dāng)前線程節(jié)點(diǎn)前面還有等待的其他線程昧甘,那么為了公平當(dāng)前線程就不會(huì)去占有這個(gè)鎖(就是說(shuō)即使這個(gè)鎖現(xiàn)在是空著的也不會(huì)去占)良拼,不像不公平鎖,它不會(huì)去判斷是否隊(duì)列還有更靠前的線程在等待
???????else if(current == getExclusiveOwnerThread()) {
???????????intnextc = c + acquires;
??????????? if(nextc <0)
???????????????throw newError("Maximum lock count exceeded");
???????????setState(nextc);
??????????? return true;
???????}
這部分跟不公平鎖是一樣的充边。
對(duì)庸推,很簡(jiǎn)單就講完了,自然它里面還提供了一些其他方法浇冰,不過(guò)都很簡(jiǎn)單啦贬媒,大家可以自己去看,至于unlock:
public voidunlock() {
???sync.release(1);
}
簡(jiǎn)單哈肘习,就是調(diào)用:
public final booleanrelease(intarg) {
???if(tryRelease(arg)) {
??????? Node h =head;
??????? if(h !=null&& h.waitStatus!=0)
??????????? unparkSuccessor(h);
??????? return true;
???}
???return false;
}
先調(diào)用tryRlease方法:
protected final booleantryRelease(intreleases) {
???intc = getState() - releases;
??? if(Thread.currentThread() != getExclusiveOwnerThread())
???????throw newIllegalMonitorStateException();
??? booleanfree =false;
??? if(c ==0) {
??????? free =true;
???????setExclusiveOwnerThread(null);
???}
??? setState(c);
??? returnfree;
}
將狀態(tài)設(shè)為當(dāng)前狀態(tài)減去releases的值际乘,如果當(dāng)前獨(dú)占鎖的線程不是當(dāng)前線程,那么一定有毛病漂佩,拋異常脖含!否則,如果減去releases之后的狀態(tài)值為0了的話投蝉,代表該鎖已經(jīng)被完全釋放了养葵,那么就把這個(gè)鎖的所有者設(shè)置為null(不再是當(dāng)前線程)。
在tryRlease成功的情況下瘩缆,如果等待隊(duì)列的頭既不為null并且它的等待狀態(tài)不是0关拒,那么就要調(diào)用AQS中的unparkSuccessor方法(可能會(huì)喚醒它的下一個(gè)節(jié)點(diǎn)),這個(gè)等待狀態(tài)的說(shuō)明在AQS那篇文章里也有講哈庸娱,這里不重復(fù)了着绊。
整個(gè)獲取鎖和釋放鎖的過(guò)程就是這樣了。
第三部分 可限時(shí)
可限時(shí)和可中斷我們還沒(méi)講涌韩,我相信可限時(shí)很好理解畔柔,就是獲取鎖的時(shí)候等待特定的時(shí)間后就不再等待直接返回蔑穴,相應(yīng)的方法:
public booleantryLock(longtimeout,TimeUnit unit)
???????throwsInterruptedException {
???returnsync.tryAcquireNanos(1,unit.toNanos(timeout));
}
第四部分 可中斷
可中斷有什么用呢渐白?就是它能夠及時(shí)地響應(yīng)中斷信號(hào)(可能從別的線程發(fā)送過(guò)來(lái)的中斷信號(hào))叨粘,ReentrantLock里面的可中斷方法是:
public voidlockInterruptibly()throwsInterruptedException {
???sync.acquireInterruptibly(1);
}
走進(jìn)acquireInterruptibly方法:
public final voidacquireInterruptibly(intarg)
???????throwsInterruptedException {
???if(Thread.interrupted())
???????throw newInterruptedException();
??? if(!tryAcquire(arg))
??????? doAcquireInterruptibly(arg);
}
其實(shí)就是判斷當(dāng)前的Thread的中斷信號(hào),如果是被中斷的狀態(tài)就直接拋異常诚欠,就不會(huì)繼續(xù)去tryAcquire了吧碾,否則還是會(huì)去等待獲取鎖睦刃,跟lock()一樣不瓶。
看看doAcquireInterruptibly方法:
private voiddoAcquireInterruptibly(intarg)
throwsInterruptedException {
finalNode node = addWaiter(Node.EXCLUSIVE);
??? booleanfailed =true;
??? try{
for(;;) {
finalNode p = node.predecessor();
??????????? if(p ==head&& tryAcquire(arg)) {
setHead(node);
???????????????p.next=null;// help GC
???????????????failed =false;
??????????????? return;
???????????}
if(shouldParkAfterFailedAcquire(p,node) &&
??????????????? parkAndCheckInterrupt())
throw newInterruptedException();
???????}
}finally{
if(failed)
cancelAcquire(node);
???}
}
在我那篇AbstractQueuedSynchronizer源碼解析講過(guò)acquireQueued方法:
final booleanacquireQueued(finalNode node, intarg) {
booleanfailed =true;
??? try{
booleaninterrupted =false;
??????? for(;;) {
finalNode p = node.predecessor();
??????????? if(p ==head&& tryAcquire(arg)) {
setHead(node);
???????????????p.next=null;// help GC
???????????????failed =false;
??????????????? returninterrupted;
???????????}
if(shouldParkAfterFailedAcquire(p,node) &&
??????????????? parkAndCheckInterrupt())
interrupted =true;
???????}
}finally{
if(failed)
cancelAcquire(node);
???}
}
仔細(xì)對(duì)比一下它們的區(qū)別,就只是當(dāng)線程中斷標(biāo)志為true的時(shí)候枚粘,acquireQueued只是保存一下當(dāng)前線程的狀態(tài)(傳遞出去以免被重置了)馅闽,而doAcquireInterruptibly直接就拋異常了。
那么中斷的好處就是馍迄,當(dāng)我們定義線程需要被中斷的時(shí)候就直接異常福也,返回,不再繼續(xù)執(zhí)行下面的操作攀圈,節(jié)約了時(shí)間暴凑,對(duì)中斷不了解的請(qǐng)看http://www.reibang.com/p/5708ac61226b
ReentrantLock也就差不多了~寫的有什么不對(duì)的地方歡迎指正。