線程阻塞喚醒工具 LockSupport使用詳解
LockSupport 簡(jiǎn)介
LockSupport
是 Java 并發(fā)編程中一個(gè)非常重要的組件,我們熟知的并發(fā)組件 Lock、線程池、CountDownLatch
等都是基于 AQS
實(shí)現(xiàn)的,而 AQS
內(nèi)部控制線程阻塞和喚醒又是通過(guò) LockSupport
來(lái)實(shí)現(xiàn)的。
從該類的注釋上也可以發(fā)現(xiàn),它是一個(gè)控制線程阻塞和喚醒的工具,與以往的不同是它解決了曾經(jīng) wait()、notify()、await()、signal()
的局限。
回顧 synchronized 和 Lock
我們知道 Java 中實(shí)現(xiàn)并發(fā)安全通常會(huì)通過(guò)這兩種加鎖的方式,對(duì)于 synchronized
加鎖的方式,如果我們想要控制線程的阻塞和喚醒是通過(guò)鎖對(duì)象的 wait()
和 notify()
方法,以下面循環(huán)交替打印 AB 為例
int status = 2; public static void main(String[] args) throws InterruptedException { TestSync obj = new TestSync(); new Thread(() -> { synchronized (obj){ while (true){ if(obj.status == 1){ obj.wait(); } System.out.println("A"); obj.status = 1; TimeUnit.SECONDS.sleep(1); obj.notify(); } } }).start(); new Thread(() -> { synchronized (obj){ while (true){ if(obj.status == 2){ obj.wait(); } System.out.println("B"); obj.status = 2; TimeUnit.SECONDS.sleep(1); obj.notify(); } } }).start(); }
如果我們使用 Lock
實(shí)現(xiàn)類,上述代碼幾乎是一樣的,只是先獲取 Condition
對(duì)象
Condition condition = lock.newCondition();
把 obj.wait()
換成 condition.await()
, obj.notify()
換成 condition.signal()
即可。
LockSupport 和 synchronized 和 Lock 的阻塞方式對(duì)比
技術(shù) | 阻塞喚醒方式 | 局限 |
---|---|---|
synchronized | 使用鎖對(duì)象的 wait()、notify() | 1. 只能用在 synchronized 包裹的同步代碼塊中 2. 必須先 wait() 才能 notify() |
Lock | 使用 condition 的 await()、signal() | 1. 只能用在 lock 鎖住的代碼塊中 2. 必須先 await() 才能 signal() |
LockSupport | park()、unpark(Thread t) | 沒(méi)有限制 |
LockSupport 的使用
下面代碼中,我們使用 LockSupport
去阻塞和喚醒線程,我們可以多次嘗試,LockSupport
的 park()
和 unpark()
方法沒(méi)有先后順序的限制,也不需要捕獲異常,也沒(méi)有限制要在什么代碼塊中才能使用。
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("A"); LockSupport.park(); System.out.println("被喚醒"); }); t1.start(); TimeUnit.SECONDS.sleep(2); new Thread(() -> { System.out.println("B"); LockSupport.unpark(t1); }).start(); }
LockSupport 注意事項(xiàng)
許可證提前發(fā)放
從該類的注釋中我們可以看到這個(gè)類存儲(chǔ)了使用它的線程的一個(gè)許可證,當(dāng)調(diào)用 park()
方法的時(shí)候會(huì)判斷當(dāng)前線程的許可證是否存在,如果存在將直接放行,否則就阻塞。
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("A"); LockSupport.park();//不會(huì)阻塞 System.out.println("被喚醒"); }); t1.start(); TimeUnit.SECONDS.sleep(2); new Thread(() -> { System.out.println("B"); System.out.println("先調(diào)用 unpark()"); LockSupport.unpark(t1); },"t2").start(); }
看這個(gè)代碼示例,這里我們?cè)?t2
中先讓線程 t1
unpark()
, 然后在 t1
中調(diào)用 park()
, 結(jié)果并不會(huì)阻塞 t1
線程。因?yàn)樵?t2
中調(diào)用 LockSupport.unpark(t1);
的時(shí)候相當(dāng)于給 t1
提前準(zhǔn)備好了許可證。
許可證不會(huì)累計(jì)
LockSupport.unpark(t1);
無(wú)論調(diào)用多少次,t1
的通行證只有一個(gè),當(dāng)在 t1
中調(diào)用兩次 park()
方法時(shí)線程依然會(huì)被阻塞。
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("A"); LockSupport.park(); LockSupport.park(); System.out.println("被喚醒"); }); t1.start(); TimeUnit.SECONDS.sleep(2); new Thread(() -> { System.out.println("B"); System.out.println("先調(diào)用 unpark()"); LockSupport.unpark(t1); LockSupport.unpark(t1); LockSupport.unpark(t1); LockSupport.unpark(t1); LockSupport.unpark(t1); },"t2").start(); }
以上述代碼為例,t1
將被阻塞。
LockSupport 底層實(shí)現(xiàn)
觀察源碼發(fā)現(xiàn) park() 和 unpark() 最底下調(diào)用的是 native() 方法,源碼在 C++ 中實(shí)現(xiàn)
@IntrinsicCandidate public native void park(boolean isAbsolute, long time); @IntrinsicCandidate public native void unpark(Object thread);
對(duì),這只是個(gè)標(biāo)題,卷不動(dòng)了,不去看 C/C++
了。。。。
結(jié)語(yǔ)
LockSupport
是 Java 并發(fā)編程中非常重要的組件,這是我們下一步閱讀 AQS(AbstractQueuedSynchronizer)
源碼的基礎(chǔ)??傊覀冎灰涀∷强刂凭€程阻塞和喚醒的工具,并且知道它與其他阻塞喚醒方式的區(qū)別即可。
以上就是線程阻塞喚醒工具 LockSupport使用詳解的詳細(xì)內(nèi)容,更多關(guān)于喚醒 LockSupport線程阻塞的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java并發(fā)容器ConcurrentLinkedQueue解析
這篇文章主要介紹了Java并發(fā)容器ConcurrentLinkedQueue解析,2023-12-12Maven打包沒(méi)有指定主類問(wèn)題(xxx.jar中沒(méi)有主清單屬性)
這篇文章主要介紹了Maven打包沒(méi)有指定主類問(wèn)題(xxx.jar中沒(méi)有主清單屬性),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04SpringBoot+EasyPoi實(shí)現(xiàn)excel導(dǎo)出功能
最新小編遇到這樣一個(gè)需求,根據(jù)檢索條件查詢列表并將結(jié)果導(dǎo)出到excel,實(shí)現(xiàn)過(guò)程也非常簡(jiǎn)單,感興趣的朋友跟隨小編一起看看吧2021-09-09@JsonFormat 實(shí)現(xiàn)日期格式自動(dòng)格式化
這篇文章主要介紹了@JsonFormat 實(shí)現(xiàn)日期格式自動(dòng)格式化,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08詳解Java對(duì)象創(chuàng)建的過(guò)程及內(nèi)存布局
今天給大家?guī)?lái)的文章是Java對(duì)象創(chuàng)建的過(guò)程及內(nèi)存布局,文中有非常詳細(xì)的圖文示例及介紹,需要的朋友可以參考下2021-06-06深入Spring Boot實(shí)現(xiàn)對(duì)Fat Jar jsp的支持
這篇文章主要介紹了深入Spring Boot實(shí)現(xiàn)對(duì)Fat Jar jsp的支持,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Servlet編程第一步之從零構(gòu)建Hello?World應(yīng)用詳細(xì)步驟+圖解
本文詳細(xì)介紹了Servlet和maven的基本概念及其在JavaWeb開(kāi)發(fā)中的應(yīng)用,首先解釋了Servlet是一個(gè)在服務(wù)器上處理請(qǐng)求的Java程序,然后介紹了maven作為管理和構(gòu)建Java項(xiàng)目的工具,需要的朋友可以參考下2024-10-10SpringAOP 設(shè)置注入的實(shí)現(xiàn)步驟
這篇文章主要介紹了SpringAOP 設(shè)置注入的實(shí)現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用Spring框架,感興趣的朋友可以了解下2021-05-05