import java.util.concurrent.TimeUnit; public class JoinWaitTest { private static int a = 0; private static int b = 100; public static void main(String... args) throws InterruptedException{ Thread t = new Thread(new WaitThread()); t.start(); t.join(); System.out.println("I'm waiting for WaitThread end."); System.out.println("The result is " + (a + b)); } static class WaitThread implements Runnable { @Override public void run() { try { for (int i = 1; i < 6; i++) { TimeUnit.SECONDS.sleep(1); a++; System.out.println(i); } } catch (InterruptedException e) { } } } }
1html
2java
3編程
4多線程
5app
I'm waiting for WaitThread end.ide
The result is 105oop
在不少狀況下,主線程生成並啓動了子線程,若是子線程裏要進行大量的耗時的運算,主線程每每將於子線程以前結束,可是若是主線程處理完其餘的事務後,須要用到子線程的處理結果,也就是主線程須要等待子線程執行完成以後再結束,這個時候就要用到join()方法了。源碼分析
本例只是個示例,演示的就是上述的過程。this
import java.util.concurrent.TimeUnit; public class JoinTest { public static void main(String... args) throws InterruptedException { Thread jt = new Thread(new JoinThread()); Thread tt = new Thread(new TimingThread()); tt.start(); tt.join(); jt.start(); } static class JoinThread implements Runnable { @Override public void run() { System.out.println("I have waited for too long."); } } static class TimingThread implements Runnable { @Override public void run() { for (int i = 0; i < 6; i++) { try { TimeUnit.SECONDS.sleep(1); System.out.println("Sleep end!"); } catch (InterruptedException e) { } } } } }
Sleep end!spa
Sleep end!
Sleep end!
Sleep end!
Sleep end!
Sleep end!
I have waited for too long.
跟源碼示例1大同小異,只不過此次是jt線程要等待tt線程結束。
在 Java中,全部對象都可以被做爲"監視器monitor"——指一個擁有一個獨佔鎖,一個入口隊列和一個等待隊列的實體entity。
全部對象的非同步方法都可以在任意時刻被任意線程調用,此時不須要考慮加鎖的問題。
而對於對象的同步方法來講,在任意時刻有且僅有一個擁有該對象獨佔鎖的線程可以調用它們。例如,一個同步方法是獨佔的。若是在線程調用某一對象的同步方法時,對象的獨佔鎖被其餘線程擁有,那麼當前線程將處於阻塞狀態,並添加到對象的入口隊列中。
只有在調用線程擁有某個對象的獨佔鎖時,纔可以調用該對象的wait(),notify()和notifyAll()方法。若是嘗試在未獲取對象鎖時調用這三個方法,那麼你將獲得一個"java.lang.IllegalMonitorStateException:current thread not owner"。
當一個線程正在某一個對象的同步方法中運行時調用了這個對象的wait()方法,那麼這個線程將釋放該對象的獨佔鎖並被放入這個對象的等待隊列,(JZ:意味着其餘線程也能夠再次調用同一個對象的wait方法)
注意,wait()方法強制當前線程釋放對象鎖。這意味着線程在調用某對象的wait()方法以前,當前線程必須已經得到該對象的鎖。所以,線程必須在某個對象的同步方法或同步代碼塊中才能調用該對象的wait()方法。wait方法是native方法,裏面的實現細節不清楚,可是線程A調用某對象B的wait方法確定默認線程A已經擁有了某對象B的鎖(可以進入同步方法或同步代碼塊就說明線程A競爭到了對象鎖)。若是跟這種默認(競爭獲得鎖)衝突,那麼JVM就會報出運行時錯誤IllegalMonitorStateException(RuntimeException)。
當某線程調用某對象的 notify()或notifyAll()方法時,任意一個(對於notify())或者全部(對於notifyAll())在該對象的等待隊列中的線程,將被轉移到該對象的入口隊列。接着這些隊列(譯者注:可能只有一個)將競爭該對象的鎖,最終得到鎖的線程繼續執行。
若是沒有線程在該對象的等待隊列中等待得到鎖,那麼notify()和notifyAll()將不起任何做用。在調用對象的notify()和notifyAll()方法以前,調用線程必須已經獲得該對象的鎖。所以,必須在某個對象的同步方法或同步代碼塊中才能調用該對象的notify()或notifyAll()方法。
對於處於某對象的等待隊列中的線程,只有當其餘線程調用此對象的notify()或notifyAll()方法時纔有機會繼續執行。
調用wait()方法的緣由一般是,調用線程但願某個特殊的狀態(或變量)被設置以後再繼續執行。調用notify()或notifyAll()方法的緣由一般是,調用線程但願告訴其餘等待中的線程:"特殊狀態已經被設置"。這個狀態做爲線程間通訊的通道,它必須是一個可變的共享狀態(或變量)。
例如,生產者線程向緩衝區中寫入數據,消費者線程從緩衝區中讀取數據。消費者線程須要等待直到生產者線程完成一次寫入操做。生產者線程須要等待消費者線程完成一次讀取操做。假設wait(),notify(),notifyAll()方法不須要加鎖就可以被調用。
此時消費者線程調用wait()正在進入狀態變量的等待隊列(譯者注:可能還未進入)。在同一時刻,生產者線程調用notify()方法打算向消費者線程通知狀態改變。那麼此時消費者線程將錯過這個通知並一直阻塞。所以,對象的wait(),notify(),notifyAll()方法必須在該對象的同步方法或同步代碼塊中被互斥地調用。
[JZ]:這也是多線程編程裏的一個經典問題,線程A進行鎖操做的過程是非原子的,線程B就進行了釋放的請求,致使線程A申請鎖後沒法釋放!
http://www.cnblogs.com/jiangz222/p/4719671.html
http://www.blogjava.net/freeman1984/archive/2011/10/14/361306.html
/** * Waits at most {@code millis} milliseconds for this thread to * die. A timeout of {@code 0} means to wait forever. * * <p> This implementation uses a loop of {@code this.wait} calls * conditioned on {@code this.isAlive}. As a thread terminates the * {@code this.notifyAll} method is invoked. It is recommended that * applications not use {@code wait}, {@code notify}, or * {@code notifyAll} on {@code Thread} instances. * * @param millis * the time to wait in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ 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; } } }
join()方法實質上就是join(0),最終執行代碼以下所示
實質上調用代碼1就至關於調用代碼2
變一下:
while (isAlive()) {
if (millis <= now) {
break;
}
wait(millis - now);
now = System.currentTimeMillis() - base;
}
base和now都是時間。base是剛執行代碼時的時間,now是執行代碼流逝的時間。判斷mills時間是否流逝完畢,流逝完畢則break跳出代碼。