【Java開發日記】我們來說說 LockSupport 的 park 和 unpark

目錄

一、LockSupport

1.1、LockSupport函數列表

1.2、基本使用

先 park 再 unpark

先 unpark 再 park

1.3、特點

與 Object 的 wait & notify 相比

二、LockSupport park & unpark原理

2.1、情況一,先調用park,再調用unpark

park 操作

unpark 操作

2.2、情況二,先調用unpark,再調用park

三、LockSupport Java源碼解析

3.1 變量說明

變量是如何獲取其實例對象的?

3.2 構造方法

3.3 兩個特殊的方法

3.4 常用方法

1、unpark(Thread thread)方法

2、park(Object blocker)方法 和?park()方法

park(Object blocker)函數中要調用兩次setBlocker函數

3、parkNanos(Object blocker, long nanos)方法 和?parkNanos(long nanos)方法

4、parkUntil(Object blocker, long deadline)方法 和?parkUntil(long deadline)方法

總結:


一、LockSupport

LockSupport是JDK中比較底層的類,用來創建鎖和其他同步工具類的基本線程阻塞原語。
Java鎖和同步器框架的核心AQS:AbstractQueuedSynchronizer,就是通過調用LockSupport.park()LockSupport.unpark()實現線程的阻塞和喚醒的。LockSupport很類似于二元信號量(只有1個許可證可供使用),如果這個許可還沒有被占用,當前線程獲取許可并繼續執行;如果許可已經被占用,當前線程阻塞,等待獲取許可。
LockSupport中的park()?和?unpark()?的作用分別是阻塞線程和解除阻塞線程,而且park()unpark()不會遇到“Thread.suspend 和 Thread.resume所可能引發的死鎖”問題。因為park()?和?unpark()有許可的存在;調用?park()?的線程和另一個試圖將其?unpark()?的線程之間的競爭將保持活性。?

1.1、LockSupport函數列表

public class LockSupport {// 返回提供給最近一次尚未解除阻塞的 park 方法調用的 blocker 對象,如果該調用不受阻塞,則返回 null。static Object getBlocker(Thread t);// 為了線程調度,禁用當前線程,除非許可可用。static void park();// 為了線程調度,在許可可用之前禁用當前線程。static void park(Object blocker);// 為了線程調度禁用當前線程,最多等待指定的等待時間,除非許可可用。static void parkNanos(long nanos);// 為了線程調度,在許可可用前禁用當前線程,并最多等待指定的等待時間。static void parkNanos(Object blocker, long nanos);// 為了線程調度,在指定的時限前禁用當前線程,除非許可可用。static void parkUntil(long deadline);// 為了線程調度,在指定的時限前禁用當前線程,除非許可可用。static void parkUntil(Object blocker, long deadline);// 如果給定線程的許可尚不可用,則使其可用。static void unpark(Thread thread);
}

說明:LockSupport是通過調用Unsafe函數中的接口實現阻塞和解除阻塞的。?

1.2、基本使用

// 暫停當前線程
LockSupport.park();
// 恢復某個線程的運行
LockSupport.unpark(暫停線程對象)

先 park 再 unpark
Thread t1 = new Thread(() -> {log.debug("start...");sleep(1);log.debug("park...");LockSupport.park();log.debug("resume...");
},"t1");
t1.start();
sleep(2);
log.debug("unpark...");
LockSupport.unpark(t1);

輸出:

18:42:52.585 c.TestParkUnpark [t1] - start...
18:42:53.589 c.TestParkUnpark [t1] - park...
18:42:54.583 c.TestParkUnpark [main] - unpark...
18:42:54.583 c.TestParkUnpark [t1] - resume...

先 unpark 再 park
Thread t1 = new Thread(() -> {log.debug("start...");sleep(2);log.debug("park...");LockSupport.park();log.debug("resume...");
}, "t1");
t1.start();
sleep(1);
log.debug("unpark...");
LockSupport.unpark(t1);

輸出:

18:43:50.765 c.TestParkUnpark [t1] - start...
18:43:51.764 c.TestParkUnpark [main] - unpark...
18:43:52.769 c.TestParkUnpark [t1] - park...
18:43:52.769 c.TestParkUnpark [t1] - resume...

1.3、特點

在調用對象的Wait之前當前線程必須先獲得該對象的監視器(Synchronized),被喚醒之后需要重新獲取到監視器才能繼續執行。而LockSupport并不需要獲取對象的監視器。?

與 Object 的 wait & notify 相比
  • 1、wait,notify 和 notifyAll 必須配合 Object Monitor 一起使用,而 park,unpark 不必。
  • 2、park & unpark 是以線程為單位來【阻塞】和【喚醒】線程,而 notify 只能隨機喚醒一個等待線程,notifyAll是喚醒所有等待線程,但不那么【精確】。
  • 3、park & unpark 可以先 unpark,而 wait & notify 不能先 notify。

因為它們本身的實現機制不一樣,所以它們之間沒有交集,也就是說LockSupport阻塞的線程,notify/notifyAll沒法喚醒。
雖然兩者用法不同,但是有一點, LockSupport 的park和Object的wait一樣也能響應中斷。

public class LockSupportTest {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {LockSupport.park();System.out.println("thread:"+Thread.currentThread().getName()+"awake");},"t1");t.start();Thread.sleep(2000);//中斷t.interrupt();}
}

二、LockSupport park & unpark原理

每個線程都會關聯一個 Parker 對象,每個 Parker 對象都各自維護了三個角色:_counter(計數器)、?_mutex(互斥量)、_cond(條件變量)。?

2.1、情況一,先調用park,再調用unpark

park 操作

  1. 當前線程調用?Unsafe.park()?方法
  2. 檢查?_counter?,本情況為 0,這時,獲得?_mutex?互斥鎖
  3. 線程進入?_cond?條件變量阻塞
  4. 設置?_counter = 0?

    unpark 操作
  5. 調用?Unsafe.unpark(Thread_0)?方法,設置?_counter?為 1

  6. 喚醒?_cond?條件變量中的?Thread_0
  7. Thread_0?恢復運行
  8. 設置?_counter?為 0?

    2.2、情況二,先調用unpark,再調用park
  9. 調用?Unsafe.unpark(Thread_0)?方法,設置?_counter?為 1

  10. 當前線程調用?Unsafe.park()?方法
  11. 檢查?_counter?,本情況為 1,這時線程無需阻塞,繼續運行
  12. 設置?_counter?為 0?

    三、LockSupport Java源碼解析

    3.1 變量說明

    public class LockSupport {// Hotspot implementation via intrinsics API//unsafe常量,設置為使用Unsafe.compareAndSwapInt進行更新//UNSAFE字段表示sun.misc.Unsafe類,一般程序中不允許直接調用private static final sun.misc.Unsafe UNSAFE;//表示parkBlocker在內存地址的偏移量private static final long parkBlockerOffset;//表示threadLocalRandomSeed在內存地址的偏移量,此變量的作用暫時還不了解private static final long SEED;//表示threadLocalRandomProbe在內存地址的偏移量,此變量的作用暫時還不了解private static final long PROBE;//表示threadLocalRandomSecondarySeed在內存地址的偏移量// 作用是 可以通過nextSecondarySeed()方法來獲取隨機數private static final long SECONDARY;
    }

    變量是如何獲取其實例對象的?
    public class LockSupport {static {try {//實例化unsafe對象UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> tk = Thread.class;//利用unsafe對象來獲取parkBlocker在內存地址的偏移量parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));//利用unsafe對象來獲取threadLocalRandomSeed在內存地址的偏移量SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));//利用unsafe對象來獲取threadLocalRandomProbe在內存地址的偏移量  PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));//利用unsafe對象來獲取threadLocalRandomSecondarySeed在內存地址的偏移量  SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));} catch (Exception ex) { throw new Error(ex); }}
    }

    由上面代碼可知這些變量是通過static代碼塊在類加載的時候就通過unsafe對象獲取其在內存地址的偏移量了。?

    3.2 構造方法

    public class LockSupport {//LockSupport只有一個私有構造函數,無法被實例化。private LockSupport() {} // Cannot be instantiated.
    }

    3.3 兩個特殊的方法

    public class LockSupport {//設置線程t的parkBlocker字段的值為argprivate static void setBlocker(Thread t, Object arg) {// Even though volatile, hotspot doesn't need a write barrier here.//盡管hotspot易變,但在這里并不需要寫屏障。UNSAFE.putObject(t, parkBlockerOffset, arg);}//獲取當前線程的Blocker值public static Object getBlocker(Thread t) {//若當前線程為空就拋出異常if (t == null)throw new NullPointerException();//利用unsafe對象獲取當前線程的Blocker值 return UNSAFE.getObjectVolatile(t, parkBlockerOffset);}
    }

    1、unpark(Thread thread)方法
    public class LockSupport {//釋放該線程的阻塞狀態,即類似釋放鎖,只不過這里是將許可設置為1public static void unpark(Thread thread) {//判斷線程是否為空if (thread != null)//釋放該線程許可UNSAFE.unpark(thread);}
    }

    2、park(Object blocker)方法 和?park()方法
    public class LockSupport {//阻塞當前線程,并且將當前線程的parkBlocker字段設置為blockerpublic static void park(Object blocker) {//獲取當前線程Thread t = Thread.currentThread();//將當前線程的parkBlocker字段設置為blockersetBlocker(t, blocker);//阻塞當前線程,第一個參數表示isAbsolute,是否為絕對時間,第二個參數就是代表時間UNSAFE.park(false, 0L);//重新可運行后再此設置BlockersetBlocker(t, null);}//無限阻塞線程,直到有其他線程調用unpark方法public static void park() {UNSAFE.park(false, 0L);}   
    }

    說明:

所以,park(Object)型函數里必須要調用setBlocker函數兩次。?

3、parkNanos(Object blocker, long nanos)方法 和?parkNanos(long nanos)方法
public class LockSupport {//阻塞當前線程nanos秒public static void parkNanos(Object blocker, long nanos) {//先判斷nanos是否大于0,小于等于0都代表無限等待if (nanos > 0) {//獲取當前線程Thread t = Thread.currentThread();//將當前線程的parkBlocker字段設置為blockersetBlocker(t, blocker);//阻塞當前線程現對時間的nanos秒UNSAFE.park(false, nanos);//將當前線程的parkBlocker字段設置為nullsetBlocker(t, null);}}   //阻塞當前線程nanos秒,現對時間public static void parkNanos(long nanos) {if (nanos > 0)UNSAFE.park(false, nanos);}   
}

4、parkUntil(Object blocker, long deadline)方法 和?parkUntil(long deadline)方法
public class LockSupport {//將當前線程阻塞絕對時間的deadline秒,并且將當前線程的parkBlockerOffset設置為blockerpublic static void parkUntil(Object blocker, long deadline) {//獲取當前線程Thread t = Thread.currentThread();//設置當前線程parkBlocker字段設置為blockersetBlocker(t, blocker);//阻塞當前線程絕對時間的deadline秒UNSAFE.park(true, deadline);//當前線程parkBlocker字段設置為nullsetBlocker(t, null);}//將當前線程阻塞絕對時間的deadline秒public static void parkUntil(long deadline) {UNSAFE.park(true, deadline);}   
}

總結:

LockSupport 和 CAS 是Java并發包中很多并發工具控制機制的基礎,它們底層其實都是依賴Unsafe實現。很多鎖的類都是基于LockSupport的park和unpark來實現的,所以了解LockSupport類是非常重要的。

如果小假的內容對你有幫助,請點贊評論收藏。創作不易,大家的支持就是我堅持下去的動力!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94182.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94182.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94182.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AGI|從“實驗室”到“生產線”:企業級AI Agent 如何突圍

在數字化轉型的深水區&#xff0c;企業級 AI Agent 正從技術概念走向產業實踐&#xff0c;成為驅動生產力變革的核心引擎。目錄 一、風口已至&#xff1a;AI Agent 的崛起邏輯與市場剛需 二、企業級AI Agent&#xff1a;核心能力與獨特價值定位 三、AI Agent 的未來目標 一、…

AtCoder Beginner Contest 417

文章目錄A A SubstringB Search and DeleteC Distance IndicatorsD Takahashis ExpectationE A Path in A DictionaryF Random GatheringG Binary CatAtCoder Beginner Contest 417A A Substring You are given an N-character string S consisting of lowercase English lett…

C++23 Concepts:用類型約束重構泛型編程的終極方案

一、開篇:模板元編程的"類型檢查困局" 某金融量化團隊曾遇到詭異bug: template<typename T> void process(T data) {static_assert(std::is_arithmetic<T>::value, "需要數值類型");// 業務邏輯... } 當調用process("hello")時…

【RK3568 看門狗驅動開發詳解】

RK3568 看門狗驅動開發詳解一、Linux 看門狗子系統架構?二、設備樹配置?三、 看門狗驅動實現四、驗證看門狗定時器&#xff08;Watchdog Timer&#xff09;是保障嵌入式系統可靠性的關鍵硬件&#xff0c;它通過定期接收 “喂狗” 信號監控系統運行狀態&#xff0c;當系統故障…

探索 Vue 3.6 新特性:Vapor Mode 與高性能 Web 應用開發

Vue 3.6 簡介 Vue.js 是一個廣受歡迎的漸進式 JavaScript 框架&#xff0c;以其簡潔的 API、靈活的組件系統和高性能著稱。Vue 3.6 是 Vue 3 系列的一個重要版本&#xff0c;引入了多項性能優化和新特性&#xff0c;尤其是備受關注的 Vapor Mode&#xff0c;這是一個無需虛擬 D…

初識prometheus

Prometheus&#xff1a;云原生時代的監控利器 在當今快速發展的云原生和微服務架構時代&#xff0c;傳統的監控系統面臨著巨大的挑戰&#xff1a;如何高效地收集海量、動態變化的指標&#xff1f;如何實時告警并快速定位問題&#xff1f;如何實現靈活的可視化和強大的數據查詢…

從源碼角度分析導致 JVM 內存泄露的 ThreadLocal

文章目錄1. 為什么需要ThreadLocal2. ThreadLocal的實現解析1.1 實現分析1.2 具體實現1.3 ThreadLocalMap中Hash沖突的解決1.3.1 Hash沖突解決的幾種方法1.3.1.1 開放定值法1.3.1.2 鏈地址法1.3.1.3再哈希法&#xff1a;1.3.1.4 建立公共溢出區1.3.2 ThreadLocal解決Hash沖突的…

React組件化的封裝

1. 組件化封裝的結構 1.1. 定義一個類(組件名必須是大寫&#xff0c;小寫會被認為是html元素), 繼續自React.Component1.2. 實現當前組件的render函數 render當中返回的jsx內容&#xff0c;就是之后React會幫助我們渲染的內容 1.3. 結構圖如下&#xff1a; data 方法render()…

嵌入式仿真教學的革新力量:深圳航天科技創新研究院引領高效學習新時代

嵌入式系統作為現代信息技術的核心基石&#xff0c;已深度融入工業控制、物聯網、智能終端等關鍵領域。高校肩負著培養嵌入式技術人才的重任&#xff0c;但傳統教學方式正面臨嚴峻挑戰&#xff1a;硬件實驗設備投入巨大、更新滯后、維護繁瑣、時空限制嚴格&#xff0c;難以滿足…

六、Linux核心服務與包管理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年8月3日 專欄&#xff1a;Linux教程 要保證一個Linux系統穩定、安全、功能完備&#xff0c;有效管理其后臺服務和軟件包是至關重要的。本文將深入介紹現代Linux系統中四個核心的管理工具&#xff1a;systemctl (服務管理)&…

【數據結構】哈希表實現

目錄 1. 哈希概念 2 哈希沖突和哈希函數 3. 負載因子 4. 將關鍵字轉為整數 5. 哈希函數 5.1直接定址法 5.2 除法散列法/除留余數法 5.3 乘法散列法&#xff08;了解&#xff09; 5.4 全域散列法&#xff08;了解&#xff09; 5.5 其他方法&#xff08;了解&#xff09…

PostgreSQL面試題及詳細答案120道(21-40)

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

數據建模及基本數據分析

目錄 &#xff08;一&#xff09;數據建模 1.以數據預測為核心的建模 2.以數據聚類為核心的建模 &#xff08;二&#xff09;基本數據分析 1.Numpy 2. Pandas 3.實例 4.Matplotlib 資料自取&#xff1a; 鏈接: https://pan.baidu.com/s/1PROmz-2hR3VCTd6Eei6lFQ?pwdy8…

電動汽車DCDC轉換器的用途及工作原理

在電動汽車的電氣架構中&#xff0c;DCDC轉換器&#xff08;直流-直流轉換器&#xff09;是一個至關重要的部件&#xff0c;負責協調高壓動力電池&#xff08;通常300V~800V&#xff09;與低壓電氣系統&#xff08;12V/24V&#xff09;之間的能量流動。它的性能直接影響整車的能…

PyTorch 應用于3D 點云數據處理匯總和點云配準示例演示

PyTorch 已廣泛應用于 3D 點云數據處理&#xff0c;特別是在深度學習驅動的任務中如&#xff1a; 分類、分割、配準、重建、姿態估計、SLAM、目標檢測 等。 傳統 3D 點云處理以 PCL、Open3D 為主&#xff0c;深度學習方法中&#xff0c;PyTorch 是構建神經網絡處理點云的核心框…

ABP VNext + Quartz.NET vs Hangfire:靈活調度與任務管理

ABP VNext Quartz.NET vs Hangfire&#xff1a;靈活調度與任務管理 &#x1f680; &#x1f4da; 目錄ABP VNext Quartz.NET vs Hangfire&#xff1a;靈活調度與任務管理 &#x1f680;? TL;DR&#x1f6e0; 環境與依賴&#x1f527; Quartz.NET 在 ABP 中接入1. 安裝與模塊…

[硬件電路-148]:數字電路 - 什么是CMOS電平、TTL電平?還有哪些其他電平標準?發展歷史?

1. CMOS電平定義&#xff1a; CMOS&#xff08;Complementary Metal-Oxide-Semiconductor&#xff09;電平基于互補金屬氧化物半導體工藝&#xff0c;由PMOS和NMOS晶體管組成。其核心特點是低功耗、高抗干擾性和寬電源電壓范圍&#xff08;通常為3V~18V&#xff09;。關鍵參數&…

0基礎網站開發技術教學(二) --(前端篇 2)--

書接上回說到的前端3種主語言以及其用法&#xff0c;這期我們再來探討一下javascript的一些編碼技術。 一) 自定義函數 假如你要使用一個功能&#xff0c;正常來說直接敲出來便可。可如果這個功能你要用不止一次呢?難道你每次都敲出來嗎?這個時侯&#xff0c;就要用到我們的自…

前端 拼多多4399筆試題目

拼多多 3 選擇題 opacity|visibity|display區別 在CSS中&#xff0c;opacity: 0 和 visibility: hidden 都可以讓元素不可見&#xff0c;但它們的行為不同&#xff1a; ? opacity: 0&#xff08;透明度為0&#xff09; 元素仍然占據空間&#xff08;不移除文檔流&#xff0…

數琨創享:全球汽車高端制造企業 QMS質量管理平臺案例

01.行業領軍者的質量升級使命在全球汽車產業鏈加速升級的浪潮中&#xff0c;質量管控能力已成為企業核心競爭力的關鍵。作為工信部認證的制造業單項冠軍示范企業&#xff0c;萬向集團始終以“全球制造、全球市場、做行業領跑者”為戰略愿景。面對奔馳、寶馬、大眾等“9N”高端客…