java?LockSupport實(shí)現(xiàn)原理示例解析
引言
前文中了解到AQS借助LockSupport.park和LockSupport.unpark完成線程的阻塞和喚醒,那么LockSupport內(nèi)部又是怎么實(shí)現(xiàn)的?這是一個(gè)什么類?
LockSupport是用于使用鎖阻塞線程的基礎(chǔ)實(shí)現(xiàn),是其他同步類的基礎(chǔ),這個(gè)類為每個(gè)使用它的線程關(guān)聯(lián)一個(gè)許可證(有點(diǎn)類似于Semaphore),如果許可證可用,線程調(diào)用park方法時(shí)會(huì)立即返回,線程正常執(zhí)行,否則當(dāng)前線程阻塞,直到有其他線程調(diào)用unpark使得許可證可用,此時(shí)線程被喚醒,再次嘗試獲取許可證,其內(nèi)部定義的park和unpark方法提供了阻塞和解決阻塞的基本實(shí)現(xiàn)。
LockSupport常見函數(shù)
如下表所示:
函數(shù)名稱 | 說明 | 備注 |
---|---|---|
void park() | 阻塞當(dāng)前線程 | 在下列情況發(fā)生時(shí)喚醒: 1.調(diào)用unpark函數(shù),釋放該線程的許可; 2.該線程被中斷; 3.設(shè)置的阻塞超時(shí)時(shí)間耗盡; 4.到達(dá)設(shè)置的指定時(shí)間 |
void park(Object blocker) | 使用指定的blocker對象阻塞當(dāng)前線程 | 喚醒條件,park中已說明 |
void parkNanos(long nanos) | 阻塞當(dāng)前線程直到超時(shí)時(shí)間耗盡,nanos為指定的超時(shí)時(shí)間 | 喚醒條件,park中已說明 |
void parkNanos(Object blocker, long nanos) | 在超時(shí)時(shí)間耗盡前,使用指定的blocker對象阻塞當(dāng)前線程,如果在到達(dá)超時(shí)時(shí)間后,許可仍不可用,則結(jié)束阻塞 | 喚醒條件,park中已說明 |
void parkUntil(long deadline) | 在指定時(shí)間前,阻塞該線程,如果在到達(dá)指定時(shí)間后,許可仍不可用,則結(jié)束阻塞 | 喚醒條件,park中已說明 |
void parkUntil(Object blocker, long deadline) | 在指定時(shí)間前,使用指定的對象阻塞該線程,如果在到達(dá)指定時(shí)間后,許可仍不可用,則結(jié)束阻塞 | 喚醒條件,park中已說明 |
void unpark(Thread thread) | 用于喚醒傳入的在阻塞中的線程 | / |
Object getBlocker(Thread t) | 獲取當(dāng)前線程的阻塞對象 | / |
void setBlocker(Thread t, Object arg) | 使用指定對象為線程設(shè)置阻塞對象 | / |
LockSupport.park
ReentrantLock中調(diào)用LockSupport.park代碼如下所示:
// AbstractQueuedSynchronizer.java private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
在LockSupport中,void park(Object blocker)
實(shí)現(xiàn)代碼如下:
// LockSupport.java public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); }
可以看到在park流程中主要包含以下過程:
獲取當(dāng)前線程
將傳入的對象設(shè)置為該線程的parkBlocker
setBlocker函數(shù)實(shí)現(xiàn)如下所示:
private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); }
可以看到這里新出現(xiàn)了UNSAFE和parkBlockerOffset兩個(gè)標(biāo)識(shí),這兩個(gè)是用來干嘛的?我們一起看看其聲明的代碼:
private static final sun.misc.Unsafe UNSAFE; private static final long parkBlockerOffset;
static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> tk = Thread.class; parkBlockerOffset = UNSAFE.objectFieldOffset (tk.getDeclaredField("parkBlocker")); ..... } catch (Exception ex) { throw new Error(ex); } }
可以看到UNSAFE對象是通過Unsafe.getUnsafe()獲取的,那么Unsafe這個(gè)類到底是干嘛的?
大家都知道Java對象在內(nèi)存中創(chuàng)建,大多數(shù)情況下我們都是通過類的對象去修改和訪問內(nèi)存中的數(shù)據(jù)的,那么如果需要直接從內(nèi)存修改某一對象的取值,應(yīng)該怎么做呢?就是使用Unsafe類,該類只允許在JDK信任的類中調(diào)用(當(dāng)前也可以用反射實(shí)例化該類對象)。
在Unsafe類中定義了兩個(gè)重要函數(shù)park和unpark,其中park用于實(shí)現(xiàn)線程阻塞,unpark用于實(shí)現(xiàn)線程喚醒(Unsafe本質(zhì)上是操作線程的Parker對象來完成線程阻塞和喚醒的,具體見參考鏈接,了解即可),上文中的parkBlockerOffset正是定義了Thread類的parkBlocker屬性成員的內(nèi)存偏移量,使用該值再結(jié)合Unsafe對象就可以實(shí)現(xiàn)直接操作內(nèi)存中的parkBlocker值的目的,Thread類中的parkBlocker聲明如下:
// Thread.java volatile Object parkBlocker;
可以得到這一環(huán)節(jié)主要是將AQS作為blocker設(shè)置到當(dāng)前線程的parkBlocker成員屬性上。
CAS底層也是通過Unsafe執(zhí)行的
執(zhí)行UNSAFE.park
結(jié)合上文可知,這步完成后,當(dāng)前線程阻塞
設(shè)置線程的parkBlocker為null
第三步中線程處于阻塞狀態(tài),當(dāng)然就不能執(zhí)行設(shè)置parkBlocker為null的操作了,那么什么時(shí)候執(zhí)行呢?當(dāng)線程從阻塞狀態(tài)喚醒時(shí),執(zhí)行該步驟,使得線程的parkBlocker對象恢復(fù)初始狀態(tài)。
LockSupport.unpark
LockSupport.unpark代碼如下所示:
// LockSupport.java public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
可以看出當(dāng)傳入的線程不為空時(shí),執(zhí)行Unsafe的park函數(shù)喚醒當(dāng)前線程,取消阻塞,此時(shí)繼續(xù)執(zhí)行park函數(shù)中的setBlocker(null),將parkBlocker成員設(shè)置為null。
參考鏈接 http://www.dbjr.com.cn/article/272073.htm
以上就是java LockSupport實(shí)現(xiàn)原理示例解析的詳細(xì)內(nèi)容,更多關(guān)于java LockSupport原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IntelliJ IDEA配置java環(huán)境及解決IDEA不能直接運(yùn)行單個(gè)JAVA文件的問題
這篇文章主要介紹了IntelliJ IDEA配置java環(huán)境及解決IDEA不能直接運(yùn)行單個(gè)JAVA文件的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07詳解備忘錄模式及其在Java設(shè)計(jì)模式編程中的實(shí)現(xiàn)
這篇文章主要介紹了詳解備忘錄模式及其在Java設(shè)計(jì)模式編程中的實(shí)現(xiàn),備忘錄模式數(shù)據(jù)的存儲(chǔ)過程中應(yīng)當(dāng)注意淺拷貝和深拷貝的問題,需要的朋友可以參考下2016-04-04Java代碼注釋規(guī)范(動(dòng)力節(jié)點(diǎn)整理)
代碼注釋是架起程序設(shè)計(jì)者與程序閱讀者之間的通信橋梁,最大限度的提高團(tuán)隊(duì)開發(fā)合作效率。也是程序代碼可維護(hù)性的重要環(huán)節(jié)之一。下面通過本文說一下我們在日常開發(fā)中使用的代碼注釋規(guī)范2017-03-03Java讀取Properties文件的七種方法的總結(jié)
這篇文章主要介紹了Java讀取Properties文件的七種方法的總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-07-07maven坐標(biāo)Dependencies和Exclusions的使用
這篇文章主要介紹了maven坐標(biāo)Dependencies和Exclusions的使用,很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java并發(fā)編程——volatile關(guān)鍵字
這篇文章主要介紹了Java并發(fā)編程——volatile關(guān)鍵字的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java并發(fā)編程,感興趣的朋友可以了解下2020-10-10