關于作者:CSDN內(nèi)容合伙人、技術專家, 從零開始做日活千萬級APP。
專注于分享各領域原創(chuàng)系列文章 ,擅長java后端、移動開發(fā)、人工智能等,希望大家多多支持。
一、導讀
我們繼續(xù)總結(jié)學習Java基礎知識,溫故知新。
二、概覽
LockSupport 是 Java SE 9 及以上版本中引入的一個線程同步工具類,用于支持同步方法,提供了多種同步機制.
LockSupport 所有的方法都是靜態(tài)方法,可以讓線程在任意位置阻塞,阻塞之后也有對應的喚醒方法。
LockSupport 底層調(diào)用的是Unsafe中的native代碼。
java.util.concurrent并發(fā)包下很多并發(fā)類的底層加鎖都是基于LockSupport,如ReentrantLock、CountDownLatch、ParkableLazyDeque 等。
三、用法
LockSupport 類中最常用的方法是 Park() 和 Unpark() 方法,分別用于阻塞和喚醒線程。
Thread a = new Thread(() -> {
System.out.println("start " + Thread.currentThread().getName());
int resule = 0;
for(int i = 0; i < 10; i ++) {
resule += i;
}
// 阻塞線程
LockSupport.park();
System.out.println(resule);
}, "a");
a.start();
try {
// TimeUnit.MILLISECONDS.sleep(1000);
} catch (Exception e) {
}
// 釋放,這里不調(diào)用的話,上面將一直鎖定
LockSupport.unpark(a);
四、原理
park和unpark都是直接調(diào)用了Unsafe的方法,
我們這么理解:這兩個方法的原理是基于一個計數(shù)器,計數(shù)器的值決定了線程的阻塞等待狀態(tài)。當計數(shù)器的值減為 0 時,表示線程可以被喚醒,執(zhí)行相應的操作。
此外,LockSupport 還提供了一些同步方法,如 Lock 和 Unlock() 方法,用于獲取和釋放鎖。Lock 方法用于獲取鎖,Unlock() 方法用于釋放鎖。與 synchronized 關鍵字不同,Lock 和 Unlock() 方法不需要同時進行,可以在任意一個方法中獲取鎖,并且可以在任意一個方法中釋放鎖,因此更加靈活和方便。
調(diào)用park后,此時線程處于waiting的狀態(tài)
public static void park() {
UNSAFE.park(false, 0L);
}
public static void park(Object blocker) {
// 獲取當前線程
Thread t = Thread.currentThread();
// 設置Blocker
setBlocker(t, blocker);
// 此后,當前線程就已經(jīng)阻塞了,后面的函數(shù)無法運行,直至unpark函數(shù)被調(diào)用
UNSAFE.park(false, 0L);
//unpark函數(shù)被調(diào)用后,繼續(xù)執(zhí)行,如果沒有第二個setBlocker,那么之后沒有調(diào)用park(Object blocker),
//而直接調(diào)用getBlocker函數(shù),得到的還是前一個park(Object blocker)設置的blocker,顯然是不符合邏輯的
setBlocker(t, null);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
Unsafe.java
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
五、線程等待和喚醒的方法
5.1 LockSupport.park()
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
} catch (Exception ex) { throw new Error(ex); }
}
Thread.sleep()和LockSupport.park()的區(qū)別
Thread.sleep()和LockSupport.park() 都是阻塞當前線程的執(zhí)行,
都 不會 釋放 當前線程占有的鎖資源;
- Thread.sleep()沒法從外部喚醒,只能自己醒過來;本身就是一個native方法;
- LockSupport.park()方法可以被另一個線程調(diào)用LockSupport.unpark()方法喚醒;* 底層是調(diào)用的Unsafe的native方法;
Thread.sleep()方法需要捕獲中斷異常。
LockSupport.park()不需要捕獲中斷異常。
5.2 Object中的wait()、notify、notifyAll
Object類中的wait、notify、notifyAll用于線程等待和喚醒的方法,都必須在sychronized內(nèi)部執(zhí)行
Object.wait()原理
在 Object.wait() 方法中,首先使用 synchronized 關鍵字鎖定當前對象,然后調(diào)用 Object.wait() 方法等待另一個線程執(zhí)行完畢并返回一個 Future 對象。在等待過程中,當前線程會一直阻塞,直到另一個線程執(zhí)行完畢并返回一個 Future 對象。當?shù)却€程執(zhí)行完畢后,F(xiàn)uture 對象的 get() 方法將返回等待線程的結(jié)果。因此,在使用 Object.wait() 方法時,需要確保等待線程已經(jīng)執(zhí)行完畢,否則可能會導致死鎖等問題。
5.3 Condition的await()方法
Java中的Condition是java.util.concurrent.locks包下的一個接口,它主要用于在多線程環(huán)境中控制線程的等待和喚醒。
線程需要獲得并持有鎖,必須在鎖塊(synchronized或lock中)
必須要先等待后喚醒,線程才能夠被喚醒。
5.4 Thread.sleep()和Object.wait()的區(qū)別
Thread.sleep()不會釋放鎖資源,到時間了會自動喚醒,然后繼續(xù)執(zhí)行;
Object.wait()會釋放鎖資源。需要另一個線程使用Object.notify()喚醒
5.5 Object.wait()和LockSupport.park()的區(qū)別
二者都會阻塞當前線程的運行。
-
Object.wait()方法需要在synchronized塊中執(zhí)行;
-
LockSupport.park()可以在任意地方執(zhí)行;
-
Object.wait()方法需要捕獲中斷異常。
-
LockSupport.park()不需要捕獲中斷異常。
-
Object.wait()不帶超時的,需要另一個線程執(zhí)行notify()來喚醒,但不一定繼續(xù)執(zhí)行后續(xù)內(nèi)容;
-
LockSupport.park()不帶超時的,需要另一個線程執(zhí)行unpark()來喚醒,一定會繼續(xù)執(zhí)行后續(xù)內(nèi)容;
5.6 Object.wait()和Condition.await()的區(qū)別
Object.wait()和Condition.await()的原理是基本一致的,不同的是Condition.await()底層是調(diào)用LockSupport.park()來實現(xiàn)阻塞當前線程的。
5.7 java中,notify/wait等方法為什么要在同步代碼塊中執(zhí)行
同步代碼塊通常由 synchronized 關鍵字修飾
在Java中,notify() 和 wait() 方法通常用于實現(xiàn)線程之間的通信和同步。這些方法通常在同步代碼塊中執(zhí)行,以確保線程安全和原子性。
在同步代碼塊中執(zhí)行 notify() 和 wait() 方法可以確保線程安全和原子性,因為它們可以保證在同一時刻只有一個線程可以執(zhí)行這些方法。如果在非同步代碼塊中執(zhí)行這些方法,則可能會導致競爭條件和死鎖等問題。
5.8 如果在wait()之前執(zhí)行了notify()會怎樣?
如果當前的線程不是此對象鎖的所有者,卻調(diào)用該對象的notify()或wait()方法時拋出IllegalMonitorStateException異常;
如果當前線程是此對象鎖的所有者,wait()將一直阻塞,因為后續(xù)將沒有其它notify()喚醒它。
5.8 如果在park()之前執(zhí)行了unpark()會怎樣?
線程不會被阻塞,直接跳過park(),繼續(xù)執(zhí)行后續(xù)內(nèi)容,大家可以自己嘗試下
Thread a = new Thread(() -> {
System.out.println("start " + Thread.currentThread().getName());
int resule = 0;
for(int i = 0; i < 10; i ++) {
resule += i;
}
LockSupport.park();
System.out.println(resule);
}, "a");
try {
// TimeUnit.MILLISECONDS.sleep(1000);
} catch (Exception e) {
}
LockSupport.unpark(a);
a.start();
LockSupport.unpark(a);
六、 推薦閱讀
Java 專欄
SQL 專欄
數(shù)據(jù)結(jié)構(gòu)與算法文章來源:http://www.zghlxwxcb.cn/news/detail-602135.html
Android學習專欄文章來源地址http://www.zghlxwxcb.cn/news/detail-602135.html
到了這里,關于探索Java并發(fā)編程利器:LockSupport,一種高效的線程阻塞與喚醒機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!