Condition.await, signal 與 Object.wait, notify 的區別

Object 類中 wait,notify 與 notifyAll 方法能夠用來實現線程之間的調度,好比在阻塞隊列(BlockingQueue)的實現中,若是隊列爲空,則全部消費者線程進行阻塞 ( wait ),若是某一個時刻隊列中新添加了一個元素,則須要喚醒某個或全部阻塞狀態的消費者線程( notify,notifyAll ),同理若是是隊列已滿,則全部生產者線程都須要阻塞,等到某個元素被消費以後又須要喚醒某個或全部正在阻塞的生產者線程java

Condition 的 await,signal, singalAll 與 Object 的 wait, notify, notifyAll 均可以實現的需求,二者在使用上也是很是相似,都須要先獲取某個鎖以後才能調用,而不一樣的是 Object wait,notify 對應的是 synchronized 方式的鎖,Condition await,singal 則對應的是 ReentrantLock (實現 Lock 接口的鎖對象)對應的鎖ide

來看下面具體的示例:使用 wait, notify 和 await, signal 方式分別實現一個簡單的隊列this

public interface SimpleQueueDemo<E> {	
	void put(E e);
	
	E take();
}

基於 Object wait, notify 的實現
spa

public class SynchronizedQueue<E> implements SimpleQueueDemo<E> {
	
	private Object[] array;
	private int index = 0;
	
	public SynchronizedQueue(int size) {
		array = new Object[size];
	}
	
	@Override
	public synchronized void put(E item) {
		while(isFull()) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		array[index++] = item;
		
		this.notifyAll();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public synchronized E take() {
		while(isEmpty()) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		E item = (E) array[0];
		array = Arrays.copyOfRange(array, 1, array.length + 1);
		array[array.length - 1] = null;
		index--;
		
		this.notifyAll();
		return item;
	}
	
	private boolean isFull() {
		return index >= array.length;
	}
	
	private boolean isEmpty() {
		return index <= 0;
	}

}

基於 await, signal 的實現線程

public class ConditionQueue<E> implements SimpleQueueDemo<E> {

	private Object[] array;
	private int index = 0;
	
	private static ReentrantLock lock = new ReentrantLock();
	
	private static Condition notEmpty = lock.newCondition();
	private static Condition notFull = lock.newCondition();
	
	public ConditionQueue(int size) {
		this.array = new Object[size];
	}
	
	@Override
	public void put(E item) {
		lock.lock();
		try {
			while(isFull()) {
				notFull.await();
			}
			
			array[index++] = item;
			
			notEmpty.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public E take() {
		lock.lock();
		try {
			while(isEmpty()) {
				notEmpty.await();
			}
			
			E item = (E) array[0];
			array = Arrays.copyOfRange(array, 1, array.length + 1);
			array[array.length - 1] = null;
			index--;
			
			notFull.signal();
			return item;
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		return null;
	}
	
	private boolean isFull() {
		return index >= array.length;
	}
	
	private boolean isEmpty() {
		return index <= 0;
	}

}

二者在使用形式和實現的功能上都很是的相似,但這裏面有一個最大的問題就是 synchronized 方式對應的 wait, notify 不能有多個謂詞條件,Lock 對應的 Condition await, signal 則能夠有多個謂詞條件code

private static ReentrantLock lock = new ReentrantLock();
	
private static Condition notEmpty = lock.newCondition();
private static Condition notFull = lock.newCondition();

沒有多個謂詞條件帶來的問題在於對象

例如隊列已滿,全部的生產者現場阻塞,某個時刻消費者消費了一個元素,則須要喚醒某個生產者線程,而經過 Object notify 方式喚醒的線程不能確保必定就是一個生產者線程,由於 notify 是隨機喚醒某一個正在該 synchronized 對應的鎖上面經過 wait 方式阻塞的線程,若是這時正好還有消費者線程也在阻塞中,則極可能喚醒的是一個消費者線程;signalAll 更是會喚醒全部在對應鎖上經過 wait 方式阻塞的線程,而無論是生產者仍是消費者線程。接口

與之不一樣的 Condition await, signal 方式則能夠對應多個謂詞條件(notEmpty, notFull),能夠很方便的實現讓生產者線程和消費者線程分別在不一樣的謂詞條件上進行等待隊列

本例中全部的生產者線程在 notEmpty 謂詞條件上等待,全部的消費者線程在 notFull 謂詞條件上等待,當隊列是滿的時候全部的生產者線程阻塞,添加元素以後則喚醒某個消費者線程,此時則不用擔憂會喚醒消費者線程it

lock.lock();
try {
	while(isFull()) {
		// 生產者線程進行阻塞
		notFull.await();
	}
	
	array[index++] = item;
	
	// 喚醒某個消費者線程
	notEmpty.signal();
} catch (InterruptedException e) {
	e.printStackTrace();
} finally {
	lock.unlock();
}
相關文章
相關標籤/搜索