# 首先從一個很有意思的問題開始:
- 問 : Thread 的join() 方法是否會釋放鎖?
- 答: 會!
# 如果一切到這里就結束了,那可能也就沒有這篇小記了,但是我的腦子卻冒出了一些奇怪的想法:
- 釋放哪個對象的鎖呢?
- 難道是釋放父線程所持有的所有對象的鎖?
-- 其實如果看了源碼,很容易明白釋放的是運行(這個地方可能有些歧義,但是我也不知道怎么說最好)join()方法的那個線程對象的鎖,不過這些都是后話,我們且往下看;
# 然后我就寫了代碼來驗證一下我的猜想, 代碼如下:
public class QQ { public static void main(String[] args) { Object oo = new Object(); Thread thread1 = new MyThread("thread1 -- ", oo); thread1.start(); synchronized (oo) { for (int i = 0; i < 100; i++) { if (i == 20) { try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " -- " + i); } } } } class MyThread extends Thread { private String name; private Object oo; public MyThread(String name, Object oo) { this.name = name; this.oo = oo; } @Override public void run() { synchronized (oo) { for (int i = 0; i < 100; i++) { System.out.println(name + i); } } } }
- 運行一下,輸出到 main -- 19 的時候,卡住了。
- 接下來我們來尋找卡住的原因;
-- 先使用jps找到出問題程序的進程號
-- jstack pid 來查看線程堆棧,結果如下圖
"Thread-1" #14 prio=5 os_prio=0 tid=0x0000000018fa9000 nid=0x3f80 waiting for monitor entry [0x0000000019b0f000] java.lang.Thread.State: BLOCKED (on object monitor) - waiting to lock <0x00000000d8a06298> (a java.lang.Object) "main" #1 prio=5 os_prio=0 tid=0x000000000228e800 nid=0x3d6c in Object.wait() [0x00000000028af000] java.lang.Thread.State: WAITING (on object monitor) - locked <0x00000000d8a06298> (a java.lang.Object)
-- 上圖中我刪掉了很多東西,只留下了一些關鍵的部分;首先我們看到 Thread-1 和 main 都在 waiting 狀態,然后再注意到 Thread-1 在等待鎖 <0x00000000d8a06298>,
但是main持有鎖<0x00000000d8a06298>, 這又是什么情況呢? 難道 main 沒有釋放鎖?
- 這時候我們就回到了最初的問題,到底join()的時候釋放的是誰的鎖,通過查看join()方法的源碼,很容易看到,其實調用的是 this.wait(),也就是說釋放的是Thread-1 這個對象的鎖
# 接着我們來用下面的代碼證實一下我們得出的結論
public class QQ { public static void main(String[] args) { Object oo = new Object(); Thread thread1 = new MyThread("thread1 -- ", oo); thread1.start(); synchronized (thread1) { for (int i = 0; i < 100; i++) { if (i == 20) { try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " -- " + i); } } } } class MyThread extends Thread { private String name; private Object oo; public MyThread(String name, Object oo) { this.name = name; this.oo = oo; } @Override public void run() { synchronized (this) { for (int i = 0; i < 100; i++) { System.out.println(name + i); } } } }
- 很容易驗證我們的猜想和理解是正確的
# 再接下來我們看一下如果調用wait() 方法,應該是怎么個情況呢;
public class QQ { public static void main(String[] args) { Object oo = new Object(); Thread thread1 = new MyThread("thread1 -- ", oo); thread1.start(); synchronized (oo) { for (int i = 0; i < 100; i++) { if (i == 20) { try { oo.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " -- " + i); } } } } class MyThread extends Thread { private String name; private Object oo; public MyThread(String name, Object oo) { this.name = name; this.oo = oo; } @Override public void run() { synchronized (oo) { for (int i = 0; i < 100; i++) { System.out.println(name + i); } oo.notifyAll(); } } }
- 乍一看,和調用join() 方法的現象一樣;
- 嗯。。。有點意思了。。。
# 最后補充一點:jstack中的Thread-1 和 我們自己定義的 thread1 是不一樣的,如果想要在jstack中顯示我們自己定義的線程名, 則需要調用Thread的setName()方法