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

