BlockingQueue接口定義了一種阻塞的FIFO queue,每一個BlockingQueue都有一個容量,讓容量滿時往BlockingQueue中添加數據時會造成阻塞,當容量爲空時取元素操作會阻塞。
ArrayBlockingQueue和LinkedBlockingQueue
ArrayBlockingQueue是一個由數組支持的有界阻塞隊列。在讀寫操作上都需要鎖住整個容器,因此吞吐量與一般的實現是相似的,適合於實現“生產者消費者”模式。
基於鏈表的阻塞隊列,同ArrayBlockingQueue類似,其內部也維持着一個數據緩衝隊列(該隊列由一個鏈表構成),當生產者往隊列中放入一個數據時,隊列會從生產者手中獲取數據,並緩存在隊列內部,而生產者立即返回;只有當隊列緩衝區達到最大值緩存容量時(LinkedBlockingQueue可以通過構造函數指定該值),纔會阻塞生產者隊列,直到消費者從隊列中消費掉一份數據,生產者線程會被喚醒,反之對於消費者這端的處理也基於同樣的原理。而LinkedBlockingQueue之所以能夠高效的處理併發數據,還因爲其對於生產者端和消費者端分別採用了獨立的鎖來控制數據同步,這也意味着在高併發的情況下生產者和消費者可以並行地操作隊列中的數據,以此來提高整個隊列的併發性能。
ArrayBlockingQueue和LinkedBlockingQueue的區別:
1. 隊列中鎖的實現不同
ArrayBlockingQueue實現的隊列中的鎖是沒有分離的,即生產和消費用的是同一個鎖;
LinkedBlockingQueue實現的隊列中的鎖是分離的,即生產用的是putLock,消費是takeLock
2. 在生產或消費時操作不同
ArrayBlockingQueue實現的隊列中在生產和消費的時候,是直接將枚舉對象插入或移除的;
LinkedBlockingQueue實現的隊列中在生產和消費的時候,需要把枚舉對象轉換爲Node<E>進行插入或移除,會影響性能
3. 隊列大小初始化方式不同
ArrayBlockingQueue實現的隊列中必須指定隊列的大小;
LinkedBlockingQueue實現的隊列中可以不指定隊列的大小,但是默認是Integer.MAX_VALUE
SynchronousQueue和PriorityBlockingQueue
都實現了BlockingQueue接口,同步隊列SynchronousQueue是無界的,是一種無緩衝的等待隊列,但是由於該Queue本身的特性,在某次添加元素後必須等待其他線程取走後才能繼續添加。PriorityBlockingQueue是優先級隊列,而不是FIFO隊列。
CountDownLaunch倒計時計數器
用給定的計數 初始化 CountDownLatch
。由於調用了
countDown()
方法,所以在當前計數到達零之前,await
方法會一直受阻塞。之後,會釋放所有等待的線程,await
的所有後續調用都將立即返回。這種現象只出現一次——計數無法被重置。
生產者消費者模型
- public class BlockingQueueTest {
- private static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5, true); //最大容量爲5的數組堵塞隊列
- //private static LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(5);
- private static CountDownLatch producerLatch; //倒計時計數器
- private static CountDownLatch consumerLatch;
- public static void main(String[] args) {
- BlockingQueueTest queueTest = new BlockingQueueTest();
- queueTest.test();
- }
- private void test(){
- producerLatch = new CountDownLatch(10); //state值爲10
- consumerLatch = new CountDownLatch(10); //state值爲10
- Thread t1 = new Thread(new ProducerTask());
- Thread t2 = new Thread(new ConsumerTask());
- //啓動線程
- t1.start();
- t2.start();
- try {
- System.out.println("producer zero...");
- producerLatch.await(); //main線程進入producerLatch倒計時等待狀態,直到state值爲0,再繼續往下執行
- System.out.println("producer end");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- try {
- System.out.println("consumer waiting...");
- consumerLatch.await(); //main線程進入consumerLatch倒計時等待狀態,直到state值爲0,再繼續往下執行
- System.out.println("consumer end");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //結束線程
- t1.interrupt();
- t2.interrupt();
- System.out.println("end");
- }
- //生產者
- class ProducerTask implements Runnable{
- private Random rnd = new Random();
- @Override
- public void run() {
- try {
- while(true){
- queue.put(rnd.nextInt(100)); //如果queue容量已滿,則當前線程會堵塞,直到有空間再繼續
- //offer方法爲非堵塞的
- //queue.offer(rnd.nextInt(100), 1, TimeUnit.SECONDS); //等待1秒後還不能加入隊列則返回失敗,放棄加入
- //queue.offer(rnd.nextInt(100));
- producerLatch.countDown(); //state值減1
- //TimeUnit.SECONDS.sleep(2); //線程休眠2秒
- }
- } catch (InterruptedException e) {
- //e.printStackTrace();
- } catch (Exception ex){
- ex.printStackTrace();
- }
- }
- }
- //消費者
- class ConsumerTask implements Runnable{
- @Override
- public void run() {
- try {
- while(true){
- Integer value = queue.take(); //如果queue爲空,則當前線程會堵塞,直到有新數據加入
- //poll方法爲非堵塞的
- //Integer value = queue.poll(1, TimeUnit.SECONDS); //等待1秒後還沒有數據可取則返回失敗,放棄獲取
- //Integer value = queue.poll();
- System.out.println("value = " + value);
- consumerLatch.countDown(); //state值減1
- TimeUnit.SECONDS.sleep(2); //線程休眠2秒
- }
- } catch (InterruptedException e) {
- //e.printStackTrace();
- } catch (Exception ex){
- ex.printStackTrace();
- }
- }
- }
- }