JUC原始碼講解:逐步解析 join()
問題丟擲
join() 在原始碼中其實是使用了 wait() 方法的, 因此, wait() 的特性 join() 都能繼承過來, 我們知道wait()有什麼特性呢?
wait()的特性:
- 會釋放鎖
- 對中斷異常敏感
- 會釋放CPU時間片
我也對wait()做了講解,想了解可移步 https://www.cnblogs.com/acdongla/p/18071381
可是, join() 真的會釋放鎖嗎? 為什麼一些例子中, 使用join() 後其他執行緒仍然被阻塞住了?join() 還有什麼需要注意的特性嗎? 讓我們帶著問題尋找答案吧
解析原始碼
進入 join() 看到這樣一段程式碼
public final void join() throws InterruptedException {
join(0);
}
深入進去,點開看看:
這裡先把原始碼貼出來,之後會逐步解析
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
先看這段程式碼:
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
可以看到, 傳遞的時間不能是負數
一個重點來了, alive() 函式出現了:
if (millis == 0) {
while (isAlive()) {
wait(0);
}
isAlive() 是對執行緒狀態的檢查, 必須是start()後的執行緒才會滿足條件.
因此,因為有 isAlive() 的檢查 , join()函式只能線上程 start() 後使用
注意!!! 這裡的 wait(0)
我們知道, wait()能釋放鎖資源, 可我們需要注意一個問題, 這裡的 wait() 釋放的是誰的鎖資源?
答: **釋放的是當前執行緒的鎖資源, 這裡的wait()是 currentThread.wait(), 是 this.wait() **
我們寫一段程式碼來說明這個問題
public class Main {
@SneakyThrows
public static void main(String[] args) {
new Thread(new YYThread(), "YY-1").start();
Thread.sleep(100);
new Thread(new YYThread(), "YY-2").start();
}
static class YYThread implements Runnable {
@SneakyThrows
@Override
public void run() {
/// 注意!!! 這裡使用鎖物件是 Main.class
synchronized (Main.clsss) {
System.out.println(Thread.currentThread().getName() + "啟動了");
Thread.currentThread().join();
}
}
}
}
注意, 在sync中使用的鎖物件是 Main.class
執行這段程式碼,會發現執行緒 "YY-2" 被阻塞了
為什麼, join()不是同wait()一樣,會釋放鎖資源嗎?
但!!! wait()釋放的是當前執行緒的鎖資源, 因此,使用全域性變數來作為鎖是不可行的
我們把run()函式做這樣的修改,就可以不阻塞了
public void run() {
/// 修改了鎖, 修改為了當前執行緒
synchronized (Thread.currentThread()) {
System.out.println(Thread.currentThread().getName() + "啟動了");
Thread.currentThread().join();
}
}
修改後,從輸出上看,兩個執行緒都正常執行了
總結
- join() 完全繼承了 wait() 方法的特性, 但需要注意的事, join() 中的原始碼使用了wait(), 代表的事 currentThread.wait(), 是"當前執行緒.wait()",因此釋放的只是當前執行緒的鎖資源
- 因為原始碼中對isAlive() 的判斷, join() 只能線上程 start() 之後才能使用
- join() 的引數不能是負數