1. 程式人生 > >java學習歷程:wait()與join()的理解誤區

java學習歷程:wait()與join()的理解誤區

之所以會寫這篇部落格,完全是因為博主一直以來對這兩個函式的理解有所模糊,於是在網上查閱資料,捋順了一些東西,在拿出來分享的同時也希望大家能對我有所指正。

一、首先上程式碼,這段程式碼是關於wait()的使用。

package test;
class Person{
	private String Name;
	private int Age; 
	private boolean isEmpty = true;
	//表示共享資源物件是否為空,如果為 true,表示需要生產,如果為 false,則有資料了,不要生產
	public synchronized void push(String aName, int aAge){
		try {
			while(isEmpty == false){
				this.wait();//不寫引數就是無限等
			}
			this.Name = aName;
			this.Age = aAge;
			Thread.sleep(10);
			
			isEmpty = false;
			this.notifyAll();//生產完畢,喚醒所有消費者
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	public synchronized void pop(){
		try {
			while(isEmpty == true){
				this.wait();
			}
			Thread.sleep(10);
			System.out.println(this.Name + " " + this.Age);

                        isEmpty = true;//設定 isEmpty為true,表示需要生產者生產物件
                        this.notifyAll();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Producer implements Runnable{
	Person p = null;
	
	public Producer(Person p){
		this.p = p;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i<20;i++){
            if(i%2==0){
                p.push("Tom", 11);
            }else{
                p.push("Marry", 22);
            }			
		}
	}
}
class Consumer implements Runnable{
	Person p = null;
	
	public Consumer(Person p){
		this.p = p;
	}
	@Override
	public void run(){
		for(int i = 0; i<20; i++){
			this.p.pop();
		}
	}
}
public class ThreadTest2 {//寫一個生產者消費者的演示程式
	public static void main(String[] args){
		Person p = new Person();
		Thread t1 = new Thread(new Producer(p));
		Thread t2 = new Thread(new Consumer(p));
		t1.start();
		t2.start();//總感覺執行緒的啟動有順序,而執行緒的執行沒有
	}
}

這是一段簡單的生產者與消費者的程式碼演示,生產一個person後消費一個person,出現Tom-11與Marry-22的交替顯示,以證明確實是生產者與消費者的程式碼段,程式碼出處http://www.cnblogs.com/ysocean/p/6896219.html,文中發現在synchronized程式碼塊中通過使用了wait()使得獲得該物件的物件鎖的執行緒進入物件的等待佇列,直至喚醒(notifyAll())。

二、上程式碼,這段程式碼是關於join()的使用。

package test;

public class ThreadTest3 {
	class ThreadImp implements Runnable {
		public synchronized void run(){
			try{
				System.out.println(Thread.currentThread().getName() + "Begin");
				Thread.sleep(10);
				/*System.out.println(Thread.currentThread().getName() + "process");
				Thread.sleep(10);*/
				System.out.println(Thread.currentThread().getName() + "end");
				Thread.sleep(10);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args)throws Exception{
		ThreadTest3 T3 = new ThreadTest3();
		ThreadImp th = T3.new ThreadImp();
		//子執行緒
		Thread t = new Thread(th,"執行緒1");
		Thread t2 = new Thread(th,"執行緒2");
		Thread t3 = new Thread(th,"執行緒3");
		Thread t4 = new Thread(th,"執行緒4");
		Thread t5 = new Thread(th,"執行緒5");
		t.start();
		t.join();
		t2.start();
		t2.join();
		t3.start();
		t3.join();
		t4.start();
		t4.join();
		t5.start();
		t5.join();
		System.out.println("the final");//main執行緒最後執行
		//t2.join();
		/*try{
			t.join(1000);//join的用法
			if (t.isAlive()) {
				System.out.println("執行緒1alive");
			} else {
				System.out.println("執行緒1dead");
			}
			System.out.println(Thread.currentThread().getName() + "finished");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
	}
}

關於join()方法,其實可以看作wait(),JDK原始碼中的實現也是基於呼叫wait()方法的,有興趣的可以自行研究一下,PS:Thread.join() 等同於Thread.wait(),無引數代表0,即無限等。上述程式碼通過呼叫join()實現了執行緒的有序執行。

三、關於這兩個方法的個人理解

舉個例子,現有兩個執行緒A與B,A中呼叫了B.join(),那麼A執行緒會停止,等B先執行完後A才開始執行。                                      那麼有人會想當然的理解成,B.join()等同於B.wait(),不就是B在wait,也就是B等待嗎,為什麼反而會讓A停運,讓B先執行呢?這也是博主之前一直很模糊的地方。要理解這裡,先假設一個物件D,需要知道其實wait()方法是D.wait(),也就是說獲取了物件D的物件鎖的執行緒釋放該物件鎖,重新進入物件的鎖獲取佇列中,與其他執行緒一起競爭該鎖,這也是為什麼第一段程式碼中,物件Person進行wait後,需要notifyAll的原因,而第二段程式碼,需要把執行緒1、2、3、4、5看作是一個個物件,PS:java中很多東西都可以看作物件,執行緒當然不例外。主執行緒main看作是呼叫這些物件的執行緒,join()是一個synchronized方法,即線上程物件1、2、3、4、5被main執行緒呼叫了join後,main執行緒會釋放該物件的物件鎖,進入該物件的鎖獲取佇列中,等待喚醒(例如該物件執行結束),這樣main執行緒才能繼續執行下去,這也是為什麼join能控制執行緒執行順序的原因所在。