Thread源码分析之join方法
2019獨角獸企業重金招聘Python工程師標準>>>
join方法示例1
源碼
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 {@Overridepublic void run() {try {for (int i = 1; i < 6; i++) {TimeUnit.SECONDS.sleep(1);a++;System.out.println(i);}} catch (InterruptedException e) {}}} }執行結果
1
2
3
4
5
I'm waiting for WaitThread end.
The result is 105
場景描述
在很多情況下,主線程生成并啟動了子線程,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結束,但是如果主線程處理完其他的事務后,需要用到子線程的處理結果,也就是主線程需要等待子線程執行完成之后再結束,這個時候就要用到join()方法了。
本例只是個示例,演示的就是上述的過程。
join方法示例2
源碼
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 {@Overridepublic void run() {System.out.println("I have waited for too long.");}}static class TimingThread implements Runnable {@Overridepublic void run() {for (int i = 0; i < 6; i++) {try {TimeUnit.SECONDS.sleep(1);System.out.println("Sleep end!");} catch (InterruptedException e) {}}}} }執行結果
Sleep end!
Sleep end!
Sleep end!
Sleep end!
Sleep end!
Sleep end!
I have waited for too long.
場景描述
跟源碼示例1大同小異,只不過這次是jt線程要等待tt線程結束。
join不帶參數源碼分析2--必須在同步方法/同步代碼塊中調用wait方法------為什么wait()【還有notify(),notifyAll()】必須在同步方法/代碼塊中調用
????在 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
join方法源碼分析
jdk源碼
/*** 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不帶參數源碼分析1--實質上是調用了wait方法
join()方法實質上就是join(0),最終執行代碼如下所示
實質上調用代碼1就相當于調用代碼2
join帶參數源碼分析
核心代碼
變一下:
while?(isAlive()) {
????????????if?(millis?<= now) {
????????????????break;
????????????}
????????????wait(millis?- now);
????????????now?= System.currentTimeMillis() - base;
????????}
分析
base和now都是時間。base是剛執行代碼時的時間,now是執行代碼流逝的時間。判斷mills時間是否流逝完畢,流逝完畢則break跳出代碼。
?
?
?
轉載于:https://my.oschina.net/u/3866531/blog/2242220
總結
以上是生活随笔為你收集整理的Thread源码分析之join方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flask基础知识点1
- 下一篇: 微信小程序 - 文字收缩与展开