java多執行緒中的join方法詳解
方法Join是幹啥用的? 簡單回答,同步,如何同步? 怎麼實現的? 下面將逐個回答。
自從接觸Java多執行緒,一直對Join理解不了。JDK是這樣說的:join public final void join(long millis)throws InterruptedException Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.大家能理解嗎? 字面意思是等待一段時間直到這個執行緒死亡,我的疑問是那個執行緒,是它本身的執行緒還是呼叫它的執行緒的,上程式碼:
/**
*
* @author vma
*/
public class JoinTest {
public static void main(String[] args) {
Thread t = new Thread(new RunnableImpl());
t.start();
try {
t.join(1000);
System.out.println("joinFinish");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class RunnableImpl implements Runnable {
@Override
public void run() {
try {
System.out.println("Begin sleep");
Thread.sleep(1000);
System.out.println("End sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果是:
Begin sleep
End sleep
joinFinish
明白了吧,當main執行緒呼叫t.join時,main執行緒等待t執行緒,等待時間是1000,如果t執行緒Sleep 2000呢
public void run() {
try {
System.out.println("Begin sleep");
// Thread.sleep(1000);
Thread.sleep(2000);
System.out.println("End sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
結果是:
Begin sleep
joinFinish
End sleep
也就是說main執行緒只等1000毫秒,不管T什麼時候結束,如果是t.join()呢, 看程式碼:
public final void join() throws InterruptedException {
join(0);
}
就是說如果是t.join() = t.join(0) 0 JDK這樣說的 A timeout of 0
其實是等到t結束後。
這個是怎麼實現的嗎? 看JDK程式碼: /**
* Waits at most <code>millis</code> milliseconds for this thread to
* die. A timeout of <code>0</code> means to wait forever.
*
* @param millis the time to wait in milliseconds.
* @exception 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方法實現是通過wait(小提示:Object 提供的方法)。 當main執行緒呼叫t.join時候,main執行緒會獲得執行緒物件t的鎖(wait 意味著拿到該物件的鎖),呼叫該物件的wait(等待時間),直到該物件喚醒main執行緒,比如退出後。
這就意味著main 執行緒呼叫t.join時,必須能夠拿到執行緒t物件的鎖,如果拿不到它是無法wait的,剛開的例子t.join(1000)不是說明了main執行緒等待1 秒,如果在它等待之前,其他執行緒獲取了t物件的鎖,它等待時間可不就是1毫秒了。上程式碼介紹: /*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package concurrentstudy;
/**
*
* @author vma
*/
public class JoinTest {
public static void main(String[] args) {
Thread t = new Thread(new RunnableImpl());
new ThreadTest(t).start();
t.start();
try {
t.join();
System.out.println("joinFinish");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadTest extends Thread {
Thread thread;
public ThreadTest(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
holdThreadLock();
}
public void holdThreadLock() {
synchronized (thread) {
System.out.println("getObjectLock");
try {
Thread.sleep(9000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("ReleaseObjectLock");
}
}
}
class RunnableImpl implements Runnable {
@Override
public void run() {
try {
System.out.println("Begin sleep");
Thread.sleep(2000);
System.out.println("End sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在main方法中 通過new ThreadTest(t).start();例項化ThreadTest 執行緒物件, 它在holdThreadLock()方法中,通過 synchronized (thread),獲取執行緒物件t的鎖,並Sleep(9000)後釋放,這就意味著,即使
main方法t.join(1000),等待一秒鐘,它必須等待ThreadTest 執行緒釋放t鎖後才能進入wait方法中,它實際等待時間是9000+1000 MS
執行結果是:
getObjectLock
Begin sleep
End sleep
ReleaseObjectLock
joinFinish
轉自:http://java.chinaitlab.com/JDK/760879.html
ps:
二、為什麼要用join()方法
主執行緒生成並起動了子執行緒,而子執行緒裡要進行大量的耗時的運算(這裡可以借鑑下執行緒的作用),當主執行緒處理完其他的事務後,需要用到子執行緒的處理結果,這個時候就要用到join();方法了。
三、join方法的作用
在網上看到有人說“將兩個執行緒合併”。這樣解釋我覺得理解起來還更麻煩。不如就借鑑下API裡的說法:
“等待該執行緒終止。”
解釋一下,是主執行緒(我在“一”裡已經命名過了)等待子執行緒的終止。也就是在子執行緒呼叫了join()方法後面的程式碼,只有等到子執行緒結束了才能執行。(Waits for this thread to die.)
四、用例項來理解
寫一個簡單的例子來看一下join()的用法,一共三個類:
1.CustomThread 類
2. CustomThread1類
3. JoinTestDemo 類,main方法所在的類。
程式碼1:
- package wxhx.csdn2;
- /**
- *
- * @author bzwm
- *
- */
- class CustomThread1 extends Thread {
- public CustomThread1() {
- super("[CustomThread1] Thread");
- };
- public void run() {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(threadName + " loop at " + i);
- Thread.sleep(1000);
- }
- System.out.println(threadName + " end.");
- } catch (Exception e) {
- System.out.println("Exception from " + threadName + ".run");
- }
- }
- }
- class CustomThread extends Thread {
- CustomThread1 t1;
- public CustomThread(CustomThread1 t1) {
- super("[CustomThread] Thread");
- this.t1 = t1;
- }
- public void run() {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- try {
- t1.join();
- System.out.println(threadName + " end.");
- } catch (Exception e) {
- System.out.println("Exception from " + threadName + ".run");
- }
- }
- }
- public class JoinTestDemo {
- public static void main(String[] args) {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- CustomThread1 t1 = new CustomThread1();
- CustomThread t = new CustomThread(t1);
- try {
- t1.start();
- Thread.sleep(2000);
- t.start();
- t.join();//在程式碼2裡,將此處注釋掉
- } catch (Exception e) {
- System.out.println("Exception from main");
- }
- System.out.println(threadName + " end!");
- }
- }
列印結果:
main start.//main方法所在的執行緒起動,但沒有馬上結束,因為呼叫t.join();,所以要等到t結束了,此執行緒才能向下執行。
[CustomThread1] Thread start.//執行緒CustomThread1起動
[CustomThread1] Thread loop at 0//執行緒CustomThread1執行
[CustomThread1] Thread loop at 1//執行緒CustomThread1執行
[CustomThread] Thread start.//執行緒CustomThread起動,但沒有馬上結束,因為呼叫t1.join();,所以要等到t1結束了,此執行緒才能向下執行。
[CustomThread1] Thread loop at 2//執行緒CustomThread1繼續執行
[CustomThread1] Thread loop at 3//執行緒CustomThread1繼續執行
[CustomThread1] Thread loop at 4//執行緒CustomThread1繼續執行
[CustomThread1] Thread end. //執行緒CustomThread1結束了
[CustomThread] Thread end.// 執行緒CustomThread在t1.join();阻塞處起動,向下繼續執行的結果
main end!//執行緒CustomThread結束,此執行緒在t.join();阻塞處起動,向下繼續執行的結果。
修改一下程式碼,得到程式碼2:(這裡只寫出修改的部分)
- public class JoinTestDemo {
- public static void main(String[] args) {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- CustomThread1 t1 = new CustomThread1();
- CustomThread t = new CustomThread(t1);
- try {
- t1.start();
- Thread.sleep(2000);
- t.start();
- // t.join();//在程式碼2裡,將此處注釋掉
- } catch (Exception e) {
- System.out.println("Exception from main");
- }
- System.out.println(threadName + " end!");
- }
- }
列印結果:
main start. // main方法所在的執行緒起動,但沒有馬上結束,這裡並不是因為join方法,而是因為Thread.sleep(2000);
[CustomThread1] Thread start. //執行緒CustomThread1起動
[CustomThread1] Thread loop at 0//執行緒CustomThread1執行
[CustomThread1] Thread loop at 1//執行緒CustomThread1執行
main end!// Thread.sleep(2000);結束,雖然線上程CustomThread執行了t1.join();,但這並不會影響到其他執行緒(這裡main方法所在的執行緒)。
[CustomThread] Thread start. //執行緒CustomThread起動,但沒有馬上結束,因為呼叫t1.join();,所以要等到t1結束了,此執行緒才能向下執行。
[CustomThread1] Thread loop at 2//執行緒CustomThread1繼續執行
[CustomThread1] Thread loop at 3//執行緒CustomThread1繼續執行
[CustomThread1] Thread loop at 4//執行緒CustomThread1繼續執行
[CustomThread1] Thread end. //執行緒CustomThread1結束了
[CustomThread] Thread end. // 執行緒CustomThread在t1.join();阻塞處起動,向下繼續執行的結果
五、從原始碼看join()方法
在CustomThread的run方法裡,執行了t1.join();,進入看一下它的JDK原始碼:
- public final void join() throws InterruptedException {
- n(0);
- }
然後進入join(0)方法:
- /**
- * Waits at most <code>millis</code> milliseconds for this thread to
- * die. A timeout of <code>0</code> means to wait forever. //注意這句
- *
- * @param millis the time to wait in milliseconds.
- * @exception InterruptedException if another 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) //引數millis為0.
- throws InterruptedException {
- long base = System.currentTimeMillis();
- long now = 0;
- if (millis < 0) {
- throw new IllegalArgumentException("timeout value is negative");
- }
- if (millis == 0) {//進入這個分支
- while (isAlive()) {//判斷本執行緒是否為活動的。這裡的本執行緒就是t1.
- wait(0);//阻塞
- }
- } else {
- while (isAlive()) {
- long delay = millis - now;
- if (delay <= 0) {
- break;
- }
- wait(delay);
- now = System.currentTimeMillis() - base;
- }
- }
- }
單純從程式碼上看,如果執行緒被生成了,但還未被起動,呼叫它的join()方法是沒有作用的。將直接繼續向下執行,這裡就不寫程式碼驗證了。