1. 程式人生 > >執行緒間的通訊wait與notify

執行緒間的通訊wait與notify

wait()方法

wait()方法使得當前執行緒必須要等待,等到另外一個執行緒呼叫notify()或者notifyAll()方法。

當前的執行緒必須擁有當前物件的monitor,也即lock,就是鎖。

執行緒呼叫wait()方法,釋放它對鎖的擁有權,然後等待另外的執行緒來通知它(通知的方式是notify()或者notifyAll() 方法),這樣它才能重新獲得鎖的擁有權和恢復執行。

要確保呼叫wait()方法的時候擁有鎖,即,wait()方法的呼叫必須放在synchronized方法或synchronized塊中。

wait()與sleep()比較

當執行緒呼叫了wait()方法時,它會釋放掉物件的鎖。

另一個會導致執行緒暫停的方法:Thread.sleep(),它會導致執行緒睡眠指定的毫秒數,但執行緒在睡眠的過程中是不會釋放 掉物件的鎖的。

notify()方法

notify()方法會喚醒一個等待當前物件的鎖的執行緒。

如果多個執行緒在等待,它們中的一個將會選擇被喚醒。這種選擇是隨意的,和具體實現有關。(執行緒等待一個物件的鎖 是由於呼叫了wait方法中的一個)。

被喚醒的執行緒是不能被執行的,需要等到當前執行緒放棄這個物件的鎖。

下面看一個程式碼示例:

public class ListAdd1 {

	private volatile static List list = new ArrayList();	
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd1 list1 = new ListAdd1();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for(int i = 0; i <10; i++){
						list1.add();
						System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素..");
						Thread.sleep(500);
					}	
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				while(true){
					if(list1.size() == 5){
						System.out.println("當前執行緒收到通知:" + Thread.currentThread().getName() + " list size = 5 執行緒停止..");
						throw new RuntimeException();
					}
				}
			}
		}, "t2");		
		
		t1.start();
		t2.start();
	}
	
}

執行結果如下:

當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
Exception in thread "t2" 當前執行緒收到通知:t2 list size = 5 執行緒停止..
java.lang.RuntimeException
    at com.fyw.thread.ListAdd1$2.run(ListAdd1.java:42)
    at java.lang.Thread.run(Unknown Source)


當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..

這種方式實現的弊端是T2執行緒需要一個死迴圈一直監聽著List的大小,如果要用wait/notify實現如下:

public class ListAdd2 {
	private volatile static List list = new ArrayList();	
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd2 list2 = new ListAdd2();
		final Object lock = new Object();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronized (lock) {
						System.out.println("t1啟動..");
						for(int i = 0; i <10; i++){
							list2.add();
							System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素..");
							Thread.sleep(500);
							if(list2.size() == 5){
								System.out.println("已經發出通知..");
								lock.notify();
							}
						}						
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					System.out.println("t2啟動..");
					if(list2.size() != 5){
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("當前執行緒:" + Thread.currentThread().getName() + "收到通知執行緒停止..");
					throw new RuntimeException();
				}
			}
		}, "t2");	
		t2.start();
		t1.start();
		
	}
	
}

執行結果如下:

t2啟動..
t1啟動..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
已經發出通知..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t2收到通知執行緒停止..
Exception in thread "t2" java.lang.RuntimeException
    at com.fyw.thread.ListAdd2$2.run(ListAdd2.java:60)
    at java.lang.Thread.run(Unknown Source)

這種實現方式也有一個問題,就是t2需要等到t1執行緒全部執行完成之後才能停止,不能做到實時性,我們可以使用CountDownLatch實現,程式碼如下:

public class ListAdd3 {
	private volatile static List list = new ArrayList();	
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd3 list2 = new ListAdd3();
        // 倒計時的次數1
		final CountDownLatch coundDownLatch = new CountDownLatch(1);
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {

					System.out.println("t1啟動..");
					for(int i = 0; i <10; i++){
						list2.add();
						System.out.println("當前執行緒:" + Thread.currentThread().getName() + "添加了一個元素..");
						Thread.sleep(500);
						if(list2.size() == 5){
							System.out.println("已經發出通知..");
							coundDownLatch.countDown();
						}
					}						
				
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {

				System.out.println("t2啟動..");
				if(list2.size() != 5){
					try {
						coundDownLatch.await();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("當前執行緒:" + Thread.currentThread().getName() + "收到通知執行緒停止..");
				throw new RuntimeException();
			
			}
		}, "t2");	
		t2.start();
		t1.start();
		
	}
	
}

執行結果如下:

t2啟動..
t1啟動..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
已經發出通知..
當前執行緒:t1添加了一個元素..
當前執行緒:t2收到通知執行緒停止..
Exception in thread "t2" java.lang.RuntimeException
    at com.fyw.thread.ListAdd3$2.run(ListAdd3.java:60)
    at java.lang.Thread.run(Unknown Source)

當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..
當前執行緒:t1添加了一個元素..