java多線程中join()方法



轉自:http://uule.iteye.com/blog/1101994

thread.Join把指定的線程加入到當前線程,可以將兩個交替執行的線程合併爲順序執行的線程。比如在線程B中調用了線程A的Join()方法,直到線程A執行完畢後,纔會繼續執行線程B。

t.join();      //使調用線程 t 在此之前執行完畢。
t.join(1000);  //等待 t 線程,等待時間是1000毫秒

 

先上一段JDK中代碼:

Java代碼 複製代碼 收藏代碼
  1. /** 
  2.      *  Waits at most <code>millis</code> milliseconds for this thread to   
  3.      * die. A timeout of <code>0</code> means to wait forever.   
  4.      */  
  5.     //此處A timeout of 0 means to wait forever 字面意思是永遠等待,其實是等到t結束後。  
  6.     public final synchronized void join(long millis)    throws InterruptedException {  
  7.         long base = System.currentTimeMillis();  
  8.         long now = 0;  
  9.   
  10.         if (millis < 0) {  
  11.             throw new IllegalArgumentException("timeout value is negative");  
  12.         }  
  13.           
  14.         if (millis == 0) {  
  15.             while (isAlive()) {  
  16.                 wait(0);  
  17.             }  
  18.         } else {  
  19.             while (isAlive()) {  
  20.                 long delay = millis - now;  
  21.                 if (delay <= 0) {  
  22.                     break;  
  23.                 }  
  24.                 wait(delay);  
  25.                 now = System.currentTimeMillis() - base;  
  26.             }  
  27.         }  
  28.     }  
/**
	 *  Waits at most <code>millis</code> milliseconds for this thread to  
     * die. A timeout of <code>0</code> means to wait forever.	
	 */
	//此處A timeout of 0 means to wait forever 字面意思是永遠等待,其實是等到t結束後。
	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方法實現是通過wait(小提示:Object 提供的方法)。 當main線程調用t.join時候,main線程會獲得線程對象t的鎖(wait 意味着拿到該對象的鎖),調用該對象的wait(等待時間),直到該對象喚醒main線程 ,比如退出後。這就意味着main 線程調用t.join時,必須能夠拿到線程t對象的鎖

 

Example1:

Java代碼 複製代碼 收藏代碼
  1. public class JoinTest implements Runnable{  
  2.       
  3.     public static int a = 0;  
  4.   
  5.     public void run() {  
  6.         for (int k = 0; k < 5; k++) {  
  7.             a = a + 1;  
  8.         }  
  9.     }  
  10.   
  11.     public static void main(String[] args) throws Exception {  
  12.         Runnable r = new JoinTest();  
  13.         Thread t = new Thread(r);  
  14.         t.start();        
  15.         System.out.println(a);  
  16.     }         
  17. }  
public class JoinTest implements Runnable{
	
	public static int a = 0;

	public void run() {
		for (int k = 0; k < 5; k++) {
			a = a + 1;
		}
	}

	public static void main(String[] args) throws Exception {
		Runnable r = new JoinTest();
		Thread t = new Thread(r);
		t.start();		
		System.out.println(a);
	}		
}

 請 問程序的輸出結果是5嗎?答案是:有可能。其實你很難遇到輸出5的時候,通常情況下都不是5。當然這也和機器有嚴重的關係。爲什麼呢?我的解釋是當主線程 main方法執行System.out.println(a);這條語句時,線程還沒有真正開始運行,或許正在爲它分配資源準備運行。因爲爲線程分配資源需要時間,而main方法執行完t.start()方法後繼續往下執行System.out.println(a);,這個時候得到的結果是a還沒有被 改變的值0 。怎樣才能讓輸出結果爲5!其實很簡單,join() 方法提供了這種功能。join() 方法,它能夠使調用該方法的線程在此之前執行完畢。

Java代碼 複製代碼 收藏代碼
  1. public static void main(String[] args) throws Exception {  
  2.         Runnable r = new JoinTest();  
  3.         Thread t = new Thread(r);  
  4.         t.start();        
  5.         t.join(); //加入join()  
  6.         System.out.println(a);  
  7.     }     
public static void main(String[] args) throws Exception {
		Runnable r = new JoinTest();
		Thread t = new Thread(r);
		t.start();		
		t.join(); //加入join()
		System.out.println(a);
	}	

 這個時候,程序輸入結果始終爲5。

爲 了證明如果不使用t.join()方法,主線程main方法的System.out.println(a);語句將搶先執行,我們可以在main方法中加入一個循環,這個循環用來延長main方法執行的時間,循環次數將嚴重取決於機器性能。如果循環次數得當,我們也可以看到a的輸出結果是5。

Java代碼 複製代碼 收藏代碼
  1. public static void main(String[] args) throws Exception {  
  2.         Runnable r = new JoinTest();  
  3.         Thread t = new Thread(r);  
  4.         t.start();        
  5.         //t.join(); //加入join()  
  6.             /* 
  7.              注意循環體內一定要有實際執行語句,否則編譯器或JVM可能優化掉你的這段代碼,視這段代 
  8.              碼爲無效。             
  9.             */  
  10.             for (int i=0; i<300; i++) {                
  11.                 System.out.print(i);  
  12.             }  
  13.             System.out.println();  
  14.         System.out.println(a);  
  15.     }         
public static void main(String[] args) throws Exception {
		Runnable r = new JoinTest();
		Thread t = new Thread(r);
		t.start();		
		//t.join(); //加入join()
			/*
			 注意循環體內一定要有實際執行語句,否則編譯器或JVM可能優化掉你的這段代碼,視這段代
			 碼爲無效。            
			*/
			for (int i=0; i<300; i++) {				
				System.out.print(i);
			}
			System.out.println();
		System.out.println(a);
	}		

 經自己測試,最後a一直是5.

本例參考:http://agio.iteye.com/blog/210600

 

Example2:join(n)

Java代碼 複製代碼 收藏代碼
  1. class RunnableImpl implements Runnable {  
  2.   
  3.     public void run() {  
  4.         try {  
  5.             System.out.println("Begin sleep");  
  6.             Thread.sleep(1000);  
  7.            System.out.println("End sleep");  
  8.         } catch (InterruptedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.   
  12.     }  
  13. }  
class RunnableImpl implements Runnable {

    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(1000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
 
Java代碼 複製代碼 收藏代碼
  1. public class JoinTest{  
  2.       
  3.     public static void main(String[] args) {  
  4.         Thread t = new Thread(new RunnableImpl());  
  5.         t.start();  
  6.         try {  
  7.             t.join(1000);  
  8.             System.out.println("joinFinish");  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();       
  11.         }  
  12.     }  
  13. }  
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) {
            e.printStackTrace();     
        }
    }
}

結果是:
Begin sleep
End sleep
joinFinish

明白了吧,當main線程調用t.join時,main線程等待t線程,等待時間是1000,如果t線程Sleep 2000呢

Java代碼 複製代碼 收藏代碼
  1. class RunnableImpl implements Runnable {  
  2.   
  3.     public void run() {  
  4.         try {  
  5.             System.out.println("Begin sleep");  
  6.             Thread.sleep(2000); //原來爲1000  
  7.            System.out.println("End sleep");  
  8.         } catch (InterruptedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.   
  12.     }  
  13. }  
class RunnableImpl implements Runnable {

    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(2000); //原來爲1000
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

結果是:
Begin sleep
joinFinish
End sleep
也就是說main線程只等1000毫秒,不管T什麼時候結束.

參考:http://blog.csdn.net/FG2006/archive/2011/05/04/6393768.aspx

 

Example3:

Java代碼 複製代碼 收藏代碼
  1. class CustomThread1 extends Thread {    
  2.       
  3.     public void run() {    
  4.         String threadName = Thread.currentThread().getName();    
  5.         System.out.println(threadName + " start.");    
  6.         try {    
  7.             for (int i = 0; i < 5; i++) {    
  8.                 System.out.println(threadName + " loop at " + i);    
  9.                 Thread.sleep(1000);    
  10.             }    
  11.             System.out.println(threadName + " end.");    
  12.         } catch (Exception e) {    
  13.             System.out.println("Exception from " + threadName + ".run");    
  14.         }    
  15.     }    
  16. }    
  17.   
  18. class CustomThread extends Thread {    
  19.     CustomThread1 t1;    
  20.     public CustomThread(CustomThread1 t1) {            
  21.         this.t1 = t1;    
  22.     }    
  23.     public void run() {    
  24.         String threadName = Thread.currentThread().getName();    
  25.         System.out.println(threadName + " start.");    
  26.         try {    
  27.             t1.join();    
  28.             System.out.println(threadName + " end.");    
  29.         } catch (Exception e) {    
  30.             System.out.println("Exception from " + threadName + ".run");    
  31.         }    
  32.     }    
  33. }    
  34.   
  35. public class JoinTestDemo {    
  36.   
  37.     public static void main(String[] args) {    
  38.         String threadName = Thread.currentThread().getName();    
  39.         System.out.println(threadName + " start.");    
  40.         CustomThread1 t1 = new CustomThread1();    
  41.         CustomThread t = new CustomThread(t1);    
  42.         try {    
  43.             t1.start();    
  44.             Thread.sleep(2000);    
  45.             t.start();    
  46.             t.join(); //在代碼2裏,將此處注釋掉    
  47.         } catch (Exception e) {    
  48.             System.out.println("Exception from main");    
  49.         }    
  50.         System.out.println(threadName + " end!");    
  51.     }    
  52. }   
 class CustomThread1 extends 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) {          
         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();阻塞處起動,向下繼續執行的結果。

 

將上例中的join註釋掉:

Java代碼 複製代碼 收藏代碼
  1. public class JoinTestDemo {    
  2.     public static void main(String[] args) {    
  3.         String threadName = Thread.currentThread().getName();    
  4.         System.out.println(threadName + " start.");    
  5.         CustomThread1 t1 = new CustomThread1();    
  6.         CustomThread t = new CustomThread(t1);    
  7.         try {    
  8.             t1.start();    
  9.             Thread.sleep(2000);    
  10.             t.start();    
  11.            //t.join();  
  12.         } catch (Exception e) {    
  13.             System.out.println("Exception from main");    
  14.         }    
  15.         System.out.println(threadName + " end!");    
  16.     }    
  17. }   
 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();
         } 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();阻塞處起動,向下繼續執行的結果

本例參考:http://blog.csdn.net/bzwm/archive/2009/02/12/3881392.aspx

 

Example4

main 線程調用t.join時,必須能夠拿到線程t對象的鎖,如果拿不到它是無法wait的 ,剛開的例子t.join(1000)不是說明了main線程等待1 秒,如果在它等待之前,其他線程獲取了t對象的鎖,它等待時間可不就是1毫秒了

Java代碼 複製代碼 收藏代碼
  1. class RunnableImpl implements Runnable {  
  2.   
  3.     public void run() {  
  4.         try {  
  5.             System.out.println("Begin sleep");  
  6.             Thread.sleep(2000);  
  7.             System.out.println("End sleep");  
  8.         } catch (InterruptedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.     }  
  12. }  
class RunnableImpl implements Runnable {

    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(2000);
            System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
Java代碼 複製代碼 收藏代碼
  1. class ThreadTest extends Thread {  
  2.   
  3.     Thread thread;  
  4.   
  5.     public ThreadTest(Thread thread) {  
  6.         this.thread = thread;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void run() {  
  11.         synchronized (thread) {  
  12.             System.out.println("getObjectLock");  
  13.             try {  
  14.                 Thread.sleep(9000);  
  15.             } catch (InterruptedException ex) {  
  16.              ex.printStackTrace();  
  17.             }  
  18.             System.out.println("ReleaseObjectLock");  
  19.         }  
  20.     }  
  21. }  
class ThreadTest extends Thread {

    Thread thread;

    public ThreadTest(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
    	synchronized (thread) {
            System.out.println("getObjectLock");
            try {
                Thread.sleep(9000);
            } catch (InterruptedException ex) {
             ex.printStackTrace();
            }
            System.out.println("ReleaseObjectLock");
        }
    }
}
 
Java代碼 複製代碼 收藏代碼
  1. public class JoinTest {  
  2.         public static void main(String[] args) {  
  3.             Thread t = new Thread(new RunnableImpl());  
  4.            new ThreadTest(t).start();  
  5.             t.start();  
  6.             try {  
  7.                 t.join();  
  8.                 System.out.println("joinFinish");  
  9.             } catch (InterruptedException e) {  
  10.                 e.printStackTrace();           
  11.             }  
  12.         }  
  13. }  
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) {
	            e.printStackTrace();	     
	        }
	    }
}

 結果:
getObjectLock
Begin sleep
End sleep
ReleaseObjectLock
joinFinish

 

在main方法中 通過new  ThreadTest(t).start()實例化 ThreadTest 線程對象, 它 通過 synchronized  (thread) ,獲取線程對象t的鎖,並Sleep(9000)後釋放,這就意味着,即使main方法t.join(1000)等待一秒鐘,它必須等待ThreadTest 線程釋放t鎖後才能進入wait方法中,它實際等待時間是9000+1000ms。

例子參考Example2來源.

發佈了49 篇原創文章 · 獲贊 13 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章