1. 程式人生 > >JAVA執行緒間協作wait、notify、notifyAll、sleep用途

JAVA執行緒間協作wait、notify、notifyAll、sleep用途

在上節中,介紹了java多執行緒中同步鎖的概念,synchronized方法和synchronized程式碼塊都是為了解決執行緒併發的問題,同一時間允許一個執行緒訪問當前類或者物件。如果涉及到執行緒間的協作通訊,就需要用到wait、notify、notifyAll方法,這三個方法是Object的方法。也就是任何物件都具有這三個方法,在java中wait、notify、notifyAll要放在synchronized方法中或者synchronized程式碼塊中使用。從功能上來說,wait方法線上程獲得物件鎖之後,主動釋放掉物件鎖,並且休眠當前執行緒。直到有執行緒呼叫notify方法之後,他才會繼續獲得物件鎖執行當前執行緒。notify方法就是用來喚醒物件鎖的,但是notify執行完之後並不是馬上執行當前喚醒的執行緒,而是等當前執行緒正在執行的執行緒執行完之後再執行。wait方法和Thread的Sleep方法作用類似,都是暫停執行緒,釋放cpu使用權。區別在於wait方法會釋放掉鎖,而sleep只是休眠當前物件,並不會讓出物件鎖。呼叫了wait的執行緒會一直等待,後面程式碼不會再執行,直到有其他執行緒呼叫notify或者notifyAll才會繼續執行。notifyAll的作用是喚醒所有的休眠的執行緒,注意只是喚醒被wait的執行緒。例項如下:

package com.thread.test;
public class MainTestThread {
	public static void main(String[] aa) {
		final MainTestThread test = new MainTestThread();
		//建立執行緒1
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					test.test1("執行緒1");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		//建立執行緒2
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					test.test1("執行緒2");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		//建立執行緒3
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					test.test1("執行緒3");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		//啟動執行緒
		thread.start();
		thread1.start();
		thread2.start();
	}

	public void test1(String thread) throws InterruptedException {
		synchronized (this) {
			for (int j = 1; j <= 5; j++) {
				//當為1的時候,表示進來新執行緒
				if (j == 1) {
					System.out.println(thread + "進入");
				}
				//為3的時候,暫時當前執行緒,並且釋放物件鎖
				if (j == 3) {
					this.wait();
				}
				System.out.println(thread + "第" + j + "執行");
				//喚醒該執行緒
				this.notify();
			}
		}
	}
}
執行結果如下:



但是也有出現這種情況:


最後一個執行緒執行了一半,這是因為notify在喚醒休眠執行緒的時候,是隨機喚醒的。當執行緒1執行到3時,wait後,另一個執行緒進入,並且喚醒執行緒1。然後又執行執行緒1(執行緒的執行是無順序的,不會因為你先呼叫就先執行某個執行緒),那麼最後一個執行緒執行到3的時候,被休眠了。沒有其他的執行緒來喚醒,一直等待下去。使用sleep來休眠當前執行執行緒:

package com.thread.test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainTestThread3 {
	public static void main(String[] aa){
		final MainTestThread3 test=new MainTestThread3();
		 Thread thread=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					test.test1("執行緒1");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		 Thread thread1=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					test.test1("執行緒2");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		 Thread thread2=new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						test.test1("執行緒3");
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			});
		thread.start();
		thread1.start();
		thread2.start();
	}
	
	public void test1(String thread) throws InterruptedException{
		SimpleDateFormat sim=new SimpleDateFormat("yyyyMMddHHmmssSS");
		synchronized (this) {
			for (int j = 1; j <= 5; j++) {
				if(j==1){
					System.out.println(thread+"進入");
				}
				if(j==3){
					String data1=sim.format(new Date());
					Thread.sleep(3000);
					String data2=sim.format(new Date());
					System.out.println("執行花費了:"+String.valueOf((Long.parseLong(data2)-Long.parseLong(data1))+"毫秒"));
				}
				System.out.println(thread+"第"+j+"執行");
			}
		}
	}
}
執行結果: