1. 程式人生 > >進階篇:同步阻塞佇列之LinkedBlockingQueue(十一)

進階篇:同步阻塞佇列之LinkedBlockingQueue(十一)

JDK為我們提供了多個阻塞佇列的實現,什麼是阻塞佇列呢? 我們都知道佇列就是一組資料的集合,而阻塞佇列的意思是,當你往佇列中取資料時,如果沒有資料,你將被阻塞,一直等到拿到資料為止;

今天我們就來看一下比較常用的遵循先進先出的阻塞佇列LinkedBlockingQueue;

//同步阻塞佇列
	//可以看到,當呼叫take()方法去拿資料時,如果裡面沒有資料,將造成阻塞
	public static void blockingQueue(){
		final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
		ExecutorService exec = Executors.newCachedThreadPool();
		//一個執行緒不斷的往佇列中取東西
		exec.execute(new Runnable() {
			public void run() {
				while( !Thread.currentThread().isInterrupted() ){
					try {
						String value = queue.take();
						System.out.println("執行緒"+Thread.currentThread()+"拿到資料:"+value);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		//一個執行緒不斷的往佇列中放東西
		exec.execute(new Runnable() {
			public void run() {
				while( !Thread.currentThread().isInterrupted() ){
					try {
						TimeUnit.MILLISECONDS.sleep(1000);
						int number = new Random().nextInt(1000);
						System.out.println("寫入資料:"+number);
						queue.put(number+"");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
	}


簡單吧!一個執行緒寫資料,一個執行緒讀資料,當讀資料的執行緒呼叫queue.take()往佇列中拿資料時,如果佇列中沒有資料,它就一直阻塞,直到寫資料的執行緒通過queue.put()方法寫入資料為止! 是不是比我們自己寫wait()和notify()要簡單好用的多呢?!

對比上一篇文章的生產者消費者的實現,如果我們採用阻塞佇列來實現的話,會變成什麼樣子呢?我們來看一下吧!

//餐廳
class BlockingRestaurant{
	public BlockingQueue<String> queue = new LinkedBlockingQueue<>();
}

//食物消費者(顧客)
class BlockingConsumer{
	//在這家餐廳就餐
	private BlockingRestaurant res = null;
	public BlockingConsumer( BlockingRestaurant res ) {
		this.res = res;
	}
	//吃食物方法
	public void eat() throws InterruptedException{
		while( !Thread.currentThread().isInterrupted() ){
			if( res.queue.take()!=null ){
				System.out.println("開始吃食物...");
				TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));//吃食物時間
				System.out.println("食物吃完了!...");
			}
		}
	}
}

//食物提供者(廚師)
class BlockingProvider{
	//在這家餐廳工作
	private BlockingRestaurant res =  null;
	public BlockingProvider( BlockingRestaurant res ) {
		this.res = res;
	}
	
	public void fry() throws InterruptedException{//炒菜
		while( !Thread.currentThread().isInterrupted() ){
			System.out.println("開始做食物...");
			TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
			res.queue.put("一份食物");
			System.out.println("食物做完了!...");
		}
	}
}
//經典生產者與消費者演示(採用佇列)
	public static void producerByQueue(){
		ExecutorService exec = Executors.newCachedThreadPool();
		BlockingRestaurant res = new BlockingRestaurant();
		final BlockingConsumer consumer = new BlockingConsumer(res);
		final BlockingProvider provider = new BlockingProvider(res);
		
		exec.execute(new Runnable() {
			public void run() {
				try {
					consumer.eat();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		exec.execute(new Runnable() {
			public void run() {
				try {
					provider.fry();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}

簡單多了吧!  Ok,這就是LinkedBlockingQueue的使用方法,後面我們會再介紹優先順序佇列,延時佇列等等多種佇列,它們將實現更有趣的功能!