首先看join()源碼:java
public final void join() throws InterruptedException { join(0); }
從源碼中能夠看出,join()直接調用了join(long)方法,join(long)源碼以下:異步
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; } } }
經過閱讀源碼能夠看出,是經過不斷輪詢的方式去觀察線程是否仍是活動線程ide
join(0)是不斷的詢問線程的狀態,直到線程銷燬join()方法纔會結束。函數
當millis>0時,不在是wait(0),而是wait(delay),delay是還剩餘的millis時間。有人會問第一次wait(delay)後不就已經通過這millis的時間,爲何還會有while執行屢次wait(delay)呢?由於這裏不只要考慮wait在delay時間後被喚醒,還要考慮到在delay時間內,被notify喚醒,喚醒後尚未執行到millis的時間,所以要屢次調用wait(delay)方法。測試
join方法底層調用的是wait方法,執行到wait方法能夠釋放鎖,而sleep方法不釋放鎖this
具體細節經過案例來說解:spa
首先定義兩個線程類:MyThreadA,MyThreadB,其中爲了保證兩個線程能夠同步執行,在MyThreadA中添加一個MyThreadB的實例變量,具體代碼以下:線程
package com.feng.example; public class MyThreadB extends Thread { @Override synchronized public void run() { try { System.out.println("begin B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.feng.example; public class MyThreadA extends Thread { private Thread b; public MyThreadA(Thread b) { this.b = b; } @Override public void run() { try { synchronized(b) {//只是爲了可以和線程b同步 System.out.println("begin A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis()); Thread.sleep(5000); System.out.println("end A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
測試類以下:code
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { Thread b = new MyThreadB(); Thread a = new MyThreadA(b); a.start(); b.start(); b.join(2000); System.out.println("main end"+"==="+System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
這個案例的結果並非惟一的。首先看第一種輸出狀況orm
begin A thread, ThreadName=Thread-1===1450080738885 end A thread, ThreadName=Thread-1===1450080743886 begin B thread, ThreadName=Thread-0===1450080743886 end B thread, ThreadName=Thread-0===1450080748886 main end===1450080748886
分析:線程a,b, b.join(2000),三者所執行的操做都是以線程實例b做爲鎖對象的,也就是說三者須要同步執行。通常來講main函數中的代碼執行要比啓動線程的代碼執行要快。(這不是重點),運行結果解釋以下:
(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)
(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成
(3)線程b與b.join(2000)方法爭搶鎖,線程b搶到鎖對象,執行run方法,直到執行完
(4)b.join(2000)得到鎖,發現線程b已經銷燬,join方法執行完畢。
(5)打印main end語句。
上述解釋中join爭搶鎖能夠是在a執行完以後,不影響輸出結果。
再看第二種運行狀況:
begin A thread, ThreadName=Thread-1===1450082014528 end A thread, ThreadName=Thread-1===1450082019529 main end===1450082019529 begin B thread, ThreadName=Thread-0===1450082019529 end B thread, ThreadName=Thread-0===1450082024530
解釋以下:
(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)
(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成
(3)線程b與b.join(2000)方法爭搶鎖,b.join搶到鎖,發現已經超過2s,所以join方法執行完成,輸出main end,釋放鎖
(4)線程b得到鎖,執行run方法
還有一隻輸出可能,這種可能很差演示,直接給出結果:
begin A thread, ThreadName=Thread-1===1450082014528 end A thread, ThreadName=Thread-1===1450082019529 begin B thread, ThreadName=Thread-0===1450082019529 main end===1450082019529 end B thread, ThreadName=Thread-0===1450082024530
解釋以下:
(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)
(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成
(3)線程b與b.join(2000)方法爭搶鎖,b.join搶到鎖,發現已經超過2s,所以join方法執行完成,釋放鎖
(4)線程b得到鎖,在主線程的輸出語句運行以前執行了線程b的run方法,這裏main線程與b線程屬於異步執行。
從上例中能夠看出join與sleep的不一樣,調用sleep時只是單純的阻塞,而且不會釋放鎖。上例同時解釋了join方法後語句提早執行的狀況。
重點理解join方法獲取鎖,執行到wait方法直接釋放鎖的問題。
主線程調用b.join方法,在join方法中是是讓線程b執行wait方法,明明是線程b等待,又如何達到讓主線程等待的功能呢?
b.wait到底wait的是什麼,線程b爲何還能執行????
自我解釋:
首先b.wait(),b只是表明一個鎖,等待的是執行b.wait()的線程。
join和wait同樣,哪一個線程執行的此操做,等待的就是哪一個線程,和調用者沒有關係,調用者只是一個鎖對象而已。