文章目錄
1 想要讀懂這篇文章必須要擁有的前置知識
《【併發編程】 — synchronized/ReentrantLock兩大特性(可重入性和不可中斷性)介紹》
《【併發編程】— interrupt、interrupted和isInterrupted使用詳解》
《【併發編程】 — Reentrantlock源碼解析2:公平鎖加鎖過程超詳細解析》
《【併發編程】 — Reentrantlock源碼解析3:公平鎖釋放鎖過程超詳細解析》
2 想寫這篇文章的原因
下面這幅圖是我在文章《【併發編程】 — Reentrantlock源碼解析3:公平鎖釋放鎖過程超詳細解析》中給出的一幅圖。
不知道大家對這幅圖中的代碼有沒有疑惑??? —> 說實話我有,而且困擾了我很久很久!!!
本篇文章將主要針對我困擾的問題進行討論,並給出我自認爲
可以自圓其說的解釋!!!
3 困擾我很久的Reentrantlock源代碼1 — 貌似無用的變量failed
我們先來看一下1中紅色標號爲5的代碼。
我的疑問如下:
可能有人會說:
在try那塊代碼裏
failed = false;這句代碼
之前有可能會拋出異常,如果拋了異常之後就會走finally代碼塊,那個時候failed還是初始值true ,那這個邏輯就有意義了。 —> 這句話說的一點沒錯,但是這是一種怎樣的情況呢???
假如你恰巧看過這篇文章《【併發編程】 — synchronized/ReentrantLock兩大特性(可重入性和不可中斷性)介紹》,那我覺得你應該是可以回答上來的:
使用Reentrantlock鎖,當某個線程正在被park時,使用stop方法中斷此線程,就正好是這種情況。
相信大家都知道使用stop方法中斷某個線程,會直接釋放掉本線程所持有的所有資源,舉個簡單的栗子來說,假如我們正在使用某個線程下載電影,如果該線程通過stop進行中斷,則原來下載的內容將全部丟失,因此可以簡單將其理解爲“暴力中斷”(自己發明的詞,見笑☺),只要調用了,就不可能恢復了 —》 應該正是基於這個原因,JDK官方將其@Deprecated
了。
但是萬一有些業務確實就需要這種“暴力中斷”呢???
看過 《【併發編程】 — synchronized/ReentrantLock兩大特性(可重入性和不可中斷性)介紹》這篇文章的應該知道,
synchronized關鍵字的處理邏輯是
,假如線程被park了,即使使用stop進行“暴力中斷”, 對不起, 沒法給你立即中斷 —》 但是當你有了搶鎖資格後,就立即給你中斷!!!
仔細想想這個邏輯 ,是不是一個挺操蛋的邏輯??? 我都採用暴力中斷了,你還不給我立即中斷,而當我有搶鎖資格了,我肯定這時候就大概率不想中斷了啊,你到立刻給我來個暴力中斷!!!
或許Doug Lea大神也覺得這個邏輯比較操蛋吧。。。所以人家在Reentrantlock中做了改進!!!
要很詳整的說出來具體的改進應該還要把cancelAcquire的源碼給搬出來,但是這裏我不打算搬出來了,所以只是簡單的,從比較宏觀的角度去闡述一下(當然私底下,我已經打過n遍斷點驗證過我的觀點了,有興趣的可以自己親自動手試試!!!
)。
假設Node鏈表中有t2和t3在排隊等着搶鎖,其結構如下,這時候突然想用stop()方法“暴力中斷”t2線程。
在不看源碼的情況下,我覺得你仔細想想應該也能想出個一二來。 首先我們應該知道既然t2要被“暴力中斷”了,那這個線程說的直白點就直接相當於沒有了。。。 那該線程在Node鏈表裏肯定也就沒必要存在了吧; —》 反過來想,假若我不處理Node鏈表,在解鎖時發現Node鏈表裏竟然除了head外還有一個線程爲null的Node,你是不是感覺也很不可理解??? —>
想到這裏其實你就知道Doug Lea大神會幹什麼了
—》 沒錯,將被stop的線程從Node鏈表裏出隊。
以上圖而言Doug Lea大神的邏輯基本如下:
- (1)既然stop了,線程會被暴力中斷,但是因爲有finally關鍵字,所以會進入到cancelAcquire()方法
- (2)將線程t2對應的節點的waitStatus置爲1
- (3)unpark線程t3 ,讓線程t3去自旋 —》 由文章《【併發編程】 — Reentrantlock源碼解析2:公平鎖加鎖過程超詳細解析》可知,由於線程t2所在Node的waitStatus此時爲1,因此會在t3自旋時,將其從Node鏈表中給踢出去 —》 由此便完成了被stop的線程t2所在節點的出隊。
4 困擾我很久的Reentrantlock源代碼2 — unpark後爲啥要來個Thread.interrupted();
這塊代碼真是困擾了很久很久很久!!!! - > 甚至都有時候都懷疑自己太鑽死牛角尖了!!!
昨天非常有幸,在B站看到了
子路老師
的講解,後又思考良久,甚至找子路老師
聊了該問題,終於找到了一個可以自圓其說的原因!!!
如果看懂了我這篇文章《【併發編程】— interrupt、interrupted和isInterrupted使用詳解》再看上面這張圖,你腦子要TMD崩潰!!!
爲啥這樣說呢???
我們這裏直接看線程t被打斷的情況,他做了個啥啊,這是!!! —》 先判斷一下是否被打斷(這裏要注意一下interrupted和isInterrupted的區別,但是對於這裏不重要!!!) —》如果是 —》我再打斷一次
-
而我想大家應該都知道使用interrupt做打斷,其實就相當於皇帝在某個妃子房間裏玩耍,突然某個太監在外面喊:萬歲,注意龍體,該去休息了。---》 但是皇帝到底停不停,全由它自己說了算!!!
- 那TMD就讓人疑惑了,Doug Lea這是要幹啥啊,閒的蛋疼?在中間傳個話???
- 如果它啥事不做,不也是這樣麼???—》 也就是說上面的流程不是和下面的流程一個樣麼???— 而且下面是不是效率還更高一些???
那Doug Lea爲啥要這樣搞呢???
研究了n天后,結合子路老師
的解答,我給出的答案是爲了代碼格式的統一,以及某些鎖少寫幾行重複代碼!!!
首先來看一下用到parkAndCheckInterrupt() 這個方法的所有方法:
- acquireQueued方法 — 獨佔鎖自旋主要邏輯
- doAcquireInterruptibly方法 — 可打斷的獨佔鎖自旋主要邏輯
- doAcquireShared方法 — 共享鎖自旋主要邏輯
- doAcquireSharedInterruptibly方法— 可打斷的共享鎖自旋主要邏輯
這算不算格式上的統一??? —》 有興趣的可以留言交流!!!
其次來簡單看一下acquireQueued
和doAcquireInterruptibly
兩個方法的簡單比較:
這算不算在某些鎖少寫幾行重複代碼 —》 有興趣的可以留言交流!!!
5 小記
有時候感覺自己像個傻子!!! 總習慣於沿着一個方向不停的鑽鑽鑽!!!
上學時老師說我適合搞研究,能做事, 但又不適合,做的太多,研究的太深, 但是卻總不發文章。
於是思考了將近半年,下定決心要去工作,所幸手頭的工作都比較簡單 。
因此想快速搭建起自己的知識體系,但是發現自己還是會時不時的鑽鑽鑽!!!—》於是學的東西慢且少,而且越學越覺得不懂的越多!!! —》 所以¥¥¥ 。。。
唉!!!唉!!!唉!!!
end!!!