join(long)與sleep(long)的區別

1.join(long)方法的源碼

首先看join()源碼:java

 public final void join() throws InterruptedException {
	join(0);
    }

從源碼中能夠看出,join()直接調用了join(long)方法,join(long)源碼以下:異步

 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;
	    }
	}
    }

經過閱讀源碼能夠看出,是經過不斷輪詢的方式去觀察線程是否仍是活動線程ide

join(0)是不斷的詢問線程的狀態,直到線程銷燬join()方法纔會結束。函數

當millis>0時,不在是wait(0),而是wait(delay),delay是還剩餘的millis時間。有人會問第一次wait(delay)後不就已經通過這millis的時間,爲何還會有while執行屢次wait(delay)呢?由於這裏不只要考慮wait在delay時間後被喚醒,還要考慮到在delay時間內,被notify喚醒,喚醒後尚未執行到millis的時間,所以要屢次調用wait(delay)方法。測試

2.比較join(long) 與 sleep(long),join方法後語句提早執行問題

join方法底層調用的是wait方法,執行到wait方法能夠釋放鎖,而sleep方法不釋放鎖this

具體細節經過案例來說解:spa

首先定義兩個線程類:MyThreadA,MyThreadB,其中爲了保證兩個線程能夠同步執行,在MyThreadA中添加一個MyThreadB的實例變量,具體代碼以下:線程

package com.feng.example;


public class MyThreadB extends Thread {


	@Override
	synchronized public void run() {

		try {
			System.out.println("begin B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());
			Thread.sleep(5000);
			System.out.println("end B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
package com.feng.example;


public class MyThreadA extends Thread {

	private Thread b;
	
	public MyThreadA(Thread b)
	{
		this.b = b;
	}
	
	@Override
	public void run() {
		try {
			synchronized(b)
			{//只是爲了可以和線程b同步
				System.out.println("begin A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());
				Thread.sleep(5000);
				System.out.println("end A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

測試類以下:code

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
				
		try {
			Thread b = new MyThreadB();
			Thread a = new MyThreadA(b);
			
			a.start();
			b.start();
			b.join(2000);
			
			System.out.println("main end"+"==="+System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
	}
}

這個案例的結果並非惟一的。首先看第一種輸出狀況orm

begin A thread, ThreadName=Thread-1===1450080738885
end A thread, ThreadName=Thread-1===1450080743886
begin B thread, ThreadName=Thread-0===1450080743886
end B thread, ThreadName=Thread-0===1450080748886
main end===1450080748886

分析:線程a,b, b.join(2000),三者所執行的操做都是以線程實例b做爲鎖對象的,也就是說三者須要同步執行。通常來講main函數中的代碼執行要比啓動線程的代碼執行要快。(這不是重點),運行結果解釋以下:

(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)

(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成

(3)線程b與b.join(2000)方法爭搶鎖,線程b搶到鎖對象,執行run方法,直到執行完

(4)b.join(2000)得到鎖,發現線程b已經銷燬,join方法執行完畢。

(5)打印main end語句。

上述解釋中join爭搶鎖能夠是在a執行完以後,不影響輸出結果。

再看第二種運行狀況:

begin A thread, ThreadName=Thread-1===1450082014528
end A thread, ThreadName=Thread-1===1450082019529
main end===1450082019529
begin B thread, ThreadName=Thread-0===1450082019529
end B thread, ThreadName=Thread-0===1450082024530

解釋以下:

(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)

(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成

(3)線程b與b.join(2000)方法爭搶鎖,b.join搶到鎖,發現已經超過2s,所以join方法執行完成,輸出main end,釋放鎖

(4)線程b得到鎖,執行run方法

還有一隻輸出可能,這種可能很差演示,直接給出結果:

begin A thread, ThreadName=Thread-1===1450082014528
end A thread, ThreadName=Thread-1===1450082019529
begin B thread, ThreadName=Thread-0===1450082019529
main end===1450082019529
end B thread, ThreadName=Thread-0===1450082024530

解釋以下:

(1)三者中b.join(2000)首先搶到b的鎖,而後代碼執行到wait(delay)釋放鎖對象(去看上面的join(long)源碼)

(2)線程a,b爭搶鎖,線程a得到鎖對象,執行synchronized(b){}語句塊,執行過程當中Thread.sleep(5000)不釋放鎖,直到執行完成

(3)線程b與b.join(2000)方法爭搶鎖,b.join搶到鎖,發現已經超過2s,所以join方法執行完成,釋放鎖

(4)線程b得到鎖,在主線程的輸出語句運行以前執行了線程b的run方法,這裏main線程與b線程屬於異步執行。

從上例中能夠看出join與sleep的不一樣,調用sleep時只是單純的阻塞,而且不會釋放鎖。上例同時解釋了join方法後語句提早執行的狀況。

重點理解join方法獲取鎖,執行到wait方法直接釋放鎖的問題。

3.存在的困惑

主線程調用b.join方法,在join方法中是是讓線程b執行wait方法,明明是線程b等待,又如何達到讓主線程等待的功能呢?

b.wait到底wait的是什麼,線程b爲何還能執行????

自我解釋:

首先b.wait(),b只是表明一個鎖,等待的是執行b.wait()的線程。

join和wait同樣,哪一個線程執行的此操做,等待的就是哪一個線程,和調用者沒有關係,調用者只是一個鎖對象而已。

相關文章
相關標籤/搜索