淺析 Java Thread.join()

 

一、在研究join的用法之前,先明確兩件事情

1.join方法定義在Thread類中,則調用者必須是一個線程,

例如:

Thread t = new CustomThread();//這裏一般是自定義的線程類

t.start();//線程起動

t.join();//此處會拋出InterruptedException異常

 

2.上面的兩行代碼也是在一個線程裏面執行的。

 

以上出現了兩個線程,一個是我們自定義的線程類,我們實現了run方法,做一些我們需要的工作;另外一個線程,生成我們自定義線程類的對象,然後執行

customThread.start();

customThread.join();

在這種情況下,兩個線程的關係是一個線程由另外一個線程生成並起動,所以我們暫且認爲第一個線程叫做“子線程”,另外一個線程叫做“主線程”。

 

二、爲什麼要用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 {   
join(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()方法是沒有作用的。將直接繼續向下執行,這裏就不寫代碼驗證了。

----2009年02月12日


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章