Java?LockSupport常用方法的源碼分析
LockSupport類(lèi)常用方法源碼
LockSupport只是一個(gè)簡(jiǎn)單的基礎(chǔ)類(lèi),位于java.util.concurrent.locks包下,多用于線程的阻塞和喚醒,因此LockSupport也被稱(chēng)為其他線程的工具類(lèi)。
LockSupport類(lèi)的源碼有標(biāo)注,LockSupport類(lèi)無(wú)法實(shí)例化。LockSupport類(lèi)的底層是有Unsafe類(lèi)實(shí)現(xiàn)的,LockSupport加載時(shí)的初始化也用到了Unsafe獲取成員的偏移量,其源碼如下:
// Hotspot implementation via intrinsics API private static final sun.misc.Unsafe UNSAFE; private static final long parkBlockerOffset; private static final long SEED; private static final long PROBE; private static final long SECONDARY; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> tk = Thread.class; parkBlockerOffset = UNSAFE.objectFieldOffset (tk.getDeclaredField("parkBlocker")); SEED = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSeed")); PROBE = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomProbe")); SECONDARY = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSecondarySeed")); } catch (Exception ex) { throw new Error(ex); } }
LockSupport類(lèi)中有一些核心的線程操作方法,多用于線程的阻塞與喚醒。
調(diào)用park()方法使線程阻塞:
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); }
調(diào)用park(Object blocker)對(duì)傳入的線程進(jìn)行阻塞
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); }
在截止時(shí)間之前阻塞傳入的某個(gè)線程:
public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); }
在nanos的時(shí)間范圍內(nèi)阻塞傳入的線程:
public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, nanos); setBlocker(t, null); } }
喚醒傳入的線程:
public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
wait/notify方法和park/unpark方法區(qū)別
LockSupport類(lèi)中的方法還有很多,在此先列舉到這里。當(dāng)我們看到阻塞和喚醒方法時(shí),我們會(huì)聯(lián)想到另一組喚醒方法wait()和notify(),這兩組方法還是有所區(qū)別的。
這里直接記錄下結(jié)論:wait和notify方法只能在同步代碼塊中使用(即必須與synchronized連用);必須先執(zhí)行wait方法,然后再執(zhí)行notify方法喚醒線程,調(diào)換順序的話線程仍處于阻塞狀態(tài)。
而park()和unpark()方法與之不同,這里可以通過(guò)代碼運(yùn)行結(jié)果來(lái)看:
package XIAOWEI; import java.util.concurrent.locks.LockSupport; ???????public class Xiaowei{ public static void main(String[] args) { Thread A = new Thread(()-> { System.out.println("線程A已經(jīng)被阻塞QWQ"); LockSupport.park(); System.out.println("線程A被線程B喚醒啦~~~"); }); A.start(); new Thread(()->{ System.out.println("線程B在喚醒線程A ing~~~"); LockSupport.unpark(A); },"B").start(); } }
那如果我們先通過(guò)線程B喚醒線程A,然后再讓線程A阻塞呢(讓線程A的阻塞休眠兩秒)?
package XIAOWEI; import java.util.concurrent.locks.LockSupport; public class Xiaowei { public static void main(String[] args) { Thread A = new Thread(()-> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程A已經(jīng)被阻塞QWQ(第二版)"); LockSupport.park(); System.out.println("線程A被線程B喚醒啦~~~(第二版)"); }); A.start(); new Thread(()->{ System.out.println("線程B在喚醒線程A ing~~~駕駕駕"); LockSupport.unpark(A); },"B").start(); } }
由上面輸出結(jié)果來(lái)看,雖然線程B先喚醒了線程A,然后線程A再開(kāi)始阻塞,但是線程A還是處于喚醒狀態(tài),這是為什么呢?
接下來(lái)我找了段LockSupport類(lèi)中的注釋?zhuān)鋵?shí)有時(shí)看看注釋也挺有意思的哈哈:
* <p>This class associates, with each thread that uses it, a permit
* (in the sense of the {@link java.util.concurrent.Semaphore
* Semaphore} class). A call to {@code park} will return immediately
* if the permit is available, consuming it in the process; otherwise
* it <em>may</em> block. A call to {@code unpark} makes the permit
* available, if it was not already available. (Unlike with Semaphores
* though, permits do not accumulate. There is at most one.)
這段話大意是說(shuō),LockSupport類(lèi)使用permits這個(gè)東西來(lái)實(shí)現(xiàn)線程的阻塞和喚醒。每一個(gè)線程都會(huì)使用到(擁有)permit,且permit的值默認(rèn)為0。接著它又說(shuō),這個(gè)概念和Semaphore信號(hào)量差不多,但是permit的值只有0和1兩個(gè)值。哦~原來(lái)是這樣。
對(duì)于上面例子,線程B調(diào)用unpark方法喚醒A后,會(huì)使得線程A的permit值為1,當(dāng)線程調(diào)用park方法使自己阻塞時(shí),發(fā)現(xiàn)自己已經(jīng)有許可(permit)了,就會(huì)繼續(xù)向下執(zhí)行業(yè)務(wù),而不會(huì)阻塞不動(dòng)。
到此這篇關(guān)于Java LockSupport常用方法的源碼分析的文章就介紹到這了,更多相關(guān)Java LockSupport內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot定義過(guò)濾器、監(jiān)聽(tīng)器、攔截器的方法
本篇文章主要介紹了SpringBoot定義過(guò)濾器、監(jiān)聽(tīng)器、攔截器的方法,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-04-04SpringCloud遠(yuǎn)程服務(wù)調(diào)用實(shí)戰(zhàn)筆記
本文給大家介紹SpringCloud遠(yuǎn)程服務(wù)調(diào)用實(shí)戰(zhàn)筆記,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-11-11Java數(shù)據(jù)結(jié)構(gòu)之簡(jiǎn)單的連接點(diǎn)(link)實(shí)現(xiàn)方法示例
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之簡(jiǎn)單的連接點(diǎn)(link)實(shí)現(xiàn)方法,涉及java指針指向節(jié)點(diǎn)的相關(guān)使用技巧,需要的朋友可以參考下2017-10-10springboot+thymeleaf國(guó)際化之LocaleResolver接口的示例
本篇文章主要介紹了springboot+thymeleaf國(guó)際化之LocaleResolver的示例 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Spring?Boot實(shí)現(xiàn)web.xml功能示例詳解
這篇文章主要介紹了Spring?Boot實(shí)現(xiàn)web.xml功能,通過(guò)本文介紹我們了解到,在Spring Boot應(yīng)用中,我們可以通過(guò)注解和編程兩種方式實(shí)現(xiàn)web.xml的功能,包括如何創(chuàng)建及注冊(cè)Servlet、Filter以及Listener等,需要的朋友可以參考下2023-09-09Java8中Stream使用的一個(gè)注意事項(xiàng)
最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中Stream使用過(guò)程中的一個(gè)注意事項(xiàng),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11springboot配置允許循環(huán)依賴(lài)問(wèn)題
這篇文章主要介紹了springboot配置允許循環(huán)依賴(lài)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Java字節(jié)與字符流永久存儲(chǔ)json數(shù)據(jù)
本篇文章給大家詳細(xì)講述了Java字節(jié)與字符流永久存儲(chǔ)json數(shù)據(jù)的方法,以及代碼分享,有興趣的參考學(xué)習(xí)下。2018-02-02Java+swing實(shí)現(xiàn)經(jīng)典貪吃蛇游戲
貪吃蛇(也叫做貪食蛇)游戲是一款休閑益智類(lèi)游戲,有PC和手機(jī)等多平臺(tái)版本。既簡(jiǎn)單又耐玩。本文將通過(guò)java的swing來(lái)實(shí)現(xiàn)這一游戲,需要的可以參考一下2022-01-01