1. 程式人生 > 程式設計 >Java wait和notifyAll實現簡單的阻塞佇列

Java wait和notifyAll實現簡單的阻塞佇列

wait,會使呼叫的執行緒進入等待狀態,會釋放所持有的物件鎖(呼叫的時候也必須先獲取到鎖,否則會丟擲異常 IllegalMonitorStateException)

notifyAll、notify,會去喚醒應當前物件而等待的執行緒,(呼叫的時候也必須先獲取到鎖,否則會丟擲異常 IllegalMonitorStateException)

順便也記錄一下join方法,呼叫join方法,會使當前執行緒進入等待,如果沒有設定等待時間,就會等待另一個執行緒執行完成才返回(ps:呼叫join方法並不一定立刻執行另一個執行緒,只是當前執行緒進入等待,然後切換下一個執行緒)

import java.util.concurrent.atomic.AtomicInteger;
/**
 * @author lhd
 */
public class BlockQueue {
	/**
  * 生產者鎖物件
  */
	private final Object addLock = new Object();
	/**
  * 消費者鎖物件
  */
	private final Object deleteLock = new Object();
	/**
  * 佇列總大小
  */
	private final Integer size = 30;
	/**
  * 資料存放
  */
	private Object[] queue = new Object[size];
	/**
  * 存放的數量,使用AtomicInteger是因為普通的int遞增遞減操作會存在非原子性的問題,會使數量異常
  */
	private AtomicInteger count = new AtomicInteger(0);
	/**
  * 生產
  * @param o 物件
  */
	public void add(Object o) {
		//獲取生產鎖,wait方法必須獲取到物件鎖後才可以呼叫,否則丟擲異常
		synchronized (addLock){
			//判斷是否超過佇列大小,超過則進入等待
			while (count.get() >= size){
				try {
					addLock.wait();
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//存放一個
			queue[count.get()] = o;
			//遞增
			int i = count.incrementAndGet();
			//列印一下日誌
			String name = Thread.currentThread().getName();
			System.out.println(name + "生產了一個,現有數量" + i);
		}
		//如果佇列有資料,則呼叫notifyAll喚醒消費者
		if (count.get() >= 1){
			//notifyAll、notify都需要先獲取物件鎖,否則會丟擲異常
			synchronized (deleteLock){
				deleteLock.notifyAll();
			}
		}
	}
	/**
  * 消費
  * @return
  */
	public Object poll(){
		Object o;
		//先獲取物件鎖,和生產者類似
		synchronized (deleteLock){
			//佇列裡沒有資料則等待
			while (count.get() <= 0){
				try {
					deleteLock.wait();
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//獲取資料
			o = queue[count.get()];
			//遞減
			int i = count.decrementAndGet();
			String name = Thread.currentThread().getName();
			System.out.println(name + "消費了一個,現有數量" + i);
		}
		//如果佇列沒有滿,則可以喚醒生產者
		if (count.get() < size){
			//需要先獲取到鎖
			synchronized (addLock){
				addLock.notifyAll();
			}
		}
		return o;
	}
	/**
  * 簡單的測試
  * @param args
  */
	public static void main(String[] args) {
		BlockQueue blockQueue = new BlockQueue();
		Thread t1 = new Thread(()-> {
			while (true){
				blockQueue.add(new Object());
			}
		}
		);
		Thread t2 = new Thread(()-> {
			while (true){
				blockQueue.add(new Object());
			}
		}
		);
		Thread t3 = new Thread(()-> {
			while (true){
				blockQueue.add(new Object());
			}
		}
		);
		Thread t4 = new Thread(()-> {
			while (true){
				blockQueue.poll();
			}
		}
		);
		Thread t5 = new Thread(()-> {
			while (true){
				blockQueue.poll();
			}
		}
		);
		Thread t6 = new Thread(()-> {
			while (true){
				blockQueue.poll();
			}
		}
		);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		t6.start();
	}
}

效果:其實這個遞增遞減操作和列印操作也不是原子操作

依次列印執行緒1,2,3

/**
 * @author lhd
 */
public class JoinTest {


 public static void main(String[] args) throws InterruptedException {
  Thread t1 = new Thread(() -> System.out.println(1));
  Thread t2 = new Thread(()-> System.out.println(2));
  Thread t3 = new Thread(()-> System.out.println(3));

  t1.start();
  t1.join();

  t2.start();
  t2.join();

  t3.start();
  t3.join();
 }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。