1. 程式人生 > >堵塞佇列之ArrayBlockingQueue和LinkedBlockingQueue解析

堵塞佇列之ArrayBlockingQueue和LinkedBlockingQueue解析

線上程池建立的時候,需要傳一個堵塞佇列來維護需要執行的執行緒任務,其中最常用的是ArrayBlockingQueue和LinkedBlockingQueue。他們都繼承了BlockingQueue介面。
這裡寫圖片描述

ArrayBlockingQueue

一個有邊界的堵塞佇列,內部使用了一個佇列來儲存元素,有takeIndex和putIndex來維護佇列頭和尾部的遊標。

    /** The queued items */
    //儲存元素的陣列
    final Object[] items;

    /** items index for next take, poll, peek or remove */
int takeIndex; /** items index for next put, offer, or add */ int putIndex; /** Number of elements in the queue */ int count; /** Main lock guarding all access */ //在取和存元素的時候會進行加鎖 final ReentrantLock lock; /** Condition for waiting takes */ //在take的時候,如果陣列為空,進行堵塞,直到陣列不為空
private final Condition notEmpty; /** Condition for waiting puts */ //在poll的時候,如果陣列滿了,進行堵塞,直到陣列有空位 private final Condition notFull; public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new
Object[capacity]; //使用鎖,預設的會使用ReentrantLock的公平鎖 //公平鎖不先嚐試獲取鎖,直接放入AQS等待佇列裡,不公平鎖會先嚐試是否可以獲取鎖,失敗才會進入等待佇列(具體實現可以檢視ReentrantLock原始碼) lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); }

新增資料可以使用offer和put方法,兩個區別是offer是實現Queue的方法,在佇列滿時候返回false,不進行其他操作。而put是實現了父類BlockingQueue的方法,在佇列滿的時候會通過notFull.await();進行堵塞,知道佇列有空位的時候後再進行判斷。
刪除也是同理,poll是實現Queue的方法,在佇列為空的時候不進行任何操作,take是實現BlockingQueue的方法,在佇列空的的時候會進行堵塞。

    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //注意這個while,要不堵塞後還是需要進行判斷
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
    //取出資料
    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

當佇列新增資料的時候,先會上鎖,然後呼叫enqueue方法新增元素。
當佇列刪除資料的時候,還是會先上鎖,然後呼叫dequeue刪除元素,其中會把items[takeIndex]置為null,這樣有助於gc,防止記憶體溢位。
其中putIndex和takeIndex和最大的長度一致時,會變為0,形成一個環,使佇列可以迴圈使用。

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }
        private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

LinkedBlockingQueue

LinkedBlockingQueue和ArrayBlockingQueue相比最大的不同是他可以作為一個無邊界的堵塞佇列,他的內部使用的連結串列的形式來儲存元素。

//內部定義了一個很簡單的Node節點來儲存資料。
static class Node<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

其次,其內部分別定義了讀寫鎖來對寫和讀進行加鎖操作,這樣相比一個鎖的好處是細化了鎖的跨度,讀寫分離,減小了鎖的競爭。

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

思考了下為什麼ArrayBlockingQueue只用了一個鎖的?我的理解是LinkedBlockingQueue是由連結串列組成操作的分別是頭尾節點,相互競爭的關係較小。而ArrayBlockingQueue是陣列,新增和刪除都是在同一個陣列上,雖然也可以用兩個鎖但是實現上需要更多的控制。

相關推薦

堵塞佇列ArrayBlockingQueueLinkedBlockingQueue解析

線上程池建立的時候,需要傳一個堵塞佇列來維護需要執行的執行緒任務,其中最常用的是ArrayBlockingQueue和LinkedBlockingQueue。他們都繼承了BlockingQueue介面。 ArrayBlockingQueue 一個有邊

併發容器ArrayBlockingQueueLinkedBlockingQueue實現原理詳解

1. ArrayBlockingQueue簡介 在多執行緒程式設計過程中,為了業務解耦和架構設計,經常會使用併發容器用於儲存多執行緒間的共享資料,這樣不僅可以保證執行緒安全,還可以簡化各個執行緒操作。例如在“生產者-消費者”問題中,會使用阻塞佇列(Blocki

JUCArrayBlockingQueueLinkedBlockingQueue

部分摘抄自 阻塞佇列 在JDK中,LinkedList或ArrayList就是佇列。但是實際使用者並不多。 阻塞佇列與我們平常接觸的普通佇列(LinkedList或ArrayList等)的最大不同點,在於阻塞佇列支出阻塞新增和阻塞刪除方法。 阻

java併發佇列ArrayBlockingQueueLinkedBlockingQueue

ArrayBlockingQueue和LinkedBlockingQueue用法上沒有什麼區別,所以就放在一起把。 特點:阻塞佇

阻塞佇列BlockingQueue以及它的兩個重要實現類ArrayBlockingQueueLinkedBlockingQueue

多執行緒環境中,通過佇列可以很容易實現資料共享,比如經典的“生產者”和“消費者”模型中,通過佇列可以很便利地實現兩者之間的資料共享。假設我們有若干生產者執行緒,另外又有若干個消費者執行緒。如果生產者執行緒需要把準備好的資料共享給消費者執行緒,利用佇列的方式來傳遞資料,就可以很方便地解決他們之間的資料

ArrayBlockingQueueLinkedBlockingQueue原始碼解析

ArrayBlockingQueue和LinkedBlockingQueue都是java.util.concurrent包中的阻塞佇列。阻塞佇列就是支援阻塞的插入和移除的容量,即在容量滿時往BlockingQueue中新增資料時會造成阻塞,當容量為空時取元素操作會阻塞。內部的

ArrayBlockingQueueLinkedBlockingQueue

分離 一個人 高效 一個 模型 空間 非阻塞 其中 影響 1、BlockingQueue接口定義了一種阻塞的FIFO queue ArrayBlockingQueue和LinkedBlockingQueue的區別: 1. 隊列中鎖的實現不同 ArrayBlockin

python爬蟲xpathlxml解析內容

上兩章說了urllib和request庫如何訪問一個頁面或者介面,從而獲取資料,如果是訪問介面,還好說,畢竟返回的json還是很好解析的,他是結構化的,我們可以把它轉化成字典來解析,但是如果返回的是xml或者html,就有點麻煩了,今天就主要說一下如果解析這些h

JDK原始碼分析—— ArrayBlockingQueue LinkedBlockingQueue

招賢納士: 我們叫數瀾 我們核心技術團隊來自阿里、華為、金蝶、移動、GE等 我們獲得來自阿里巴巴集團聯合創始人、湖畔山南總裁謝世煌、IDG合夥人牛奎光、洪泰等投資 我們的官網:https://www.dtwave.com 我們提供:期權、五險一金、試用期全薪、商業保險、免

小BO學習筆記ConcurrentLinkedQueueLinkedBlockingQueue的使用比較

使用環境: 在Java多執行緒應用中,佇列的使用率很高,多數生產消費模型的首選資料結構就是佇列(先進先出)。Java提供的執行緒安全的Queue可以分為阻塞佇列和非阻塞佇列,其中阻塞佇列的典型例子是BlockingQueue,非阻塞佇列的典型例子是Concu

ArrayBlockingQueueLinkedBlockingQueue的區別及使用

BlockingQueue介面定義了一種阻塞的FIFO queue,每一個BlockingQueue都有一個容量,讓容量滿時往BlockingQueue中新增資料時會造成阻塞,當容量為空時取元素操作會阻塞。 ArrayBlockingQueue是一個由陣列支援的有界阻塞佇

第五章 SpringMVCViewResolverView解析

          過完年了,本來是想在年前將SpringMVC系列寫完的,只是在接近年末的時候沒有了一種學習心態,這兩天看了一下ViewResolver原始碼,就想盡快將這篇部落格寫出,也好完結SpringMVC的系列部落格並開始下面的學習。           自己寫的

Java併發包原始碼學習系列:阻塞佇列實現ArrayBlockingQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Javadom4j的簡單解析生成xml的應用

util 讀寫 pro artifact gettext depend bject sta rgs   一、dom4j是一個Java的XML API,是jdom的升級品,用來讀寫XML文件的。dom4j是一個十分優秀的JavaXML API,具有性能優異、功能強大和極其易使

Djangourl上的include,URL命名反向解析,命名空間 以及圖書管理系統刪除功能二合一方法

實例 系統 code 應用 兩個 exce app number 管理系統 include其他的URLconfs #At any point, your urlpatterns can “include” other URLconf modules. This #es

資料結構JAVA版佇列

一、涉及的內容大綱 二、簡單介紹棧、佇列和其他資料結構的不同 1 對於其他的資料結構而言,都適用與資料庫應用中作資料記錄。但是因為棧和佇列的生命週期比那些資料庫型別的資料結構要短,所以他們只是在程式的操作期間才會建立和執行,在完成任務之後就會被銷燬。所以棧和佇列更多的是用於構思演算法的

訊息佇列RabbitMQ - 簡介安裝

       訊息佇列:是簡單的生產者和消費者模式,它的出現是讓各個服務板塊之間解耦和訊息通知。比如,我們一般生成服務板塊中的資料存在有:資料庫,靜態檔案,搜尋系統,hdfs等,那麼如果資料庫中的資料發生了變化,怎麼把這個訊息推送給其他的資料儲存單元呢?如果單

輕鬆理解 - 中高階java開發必知必會 掌握 java阻塞隊(ArrayBlockingQueueLinkedBlockingQueue)

在java開發中有些特殊場景下適用於阻塞佇列如: 多執行緒環境中,通過佇列可以很容易實現資料共享,比如經典的“生產者”和“消費者”模型中,通過佇列可以很便利地實現兩者之間的資料共享。假設我們有若干生產者執行緒,另外又有若干個消費者執行緒。如果生產者執行緒需要把準備好的資料共享給消費者執行緒,利用

原始碼解析關於java阻塞容器:ArrayBlockingQueueLinkedBlockingQueue

Java的阻塞容器介紹JDK18 一ArrayBlockingQueue 類的定義 重要的成員變數 初始化 一些重要的非公開的方法

Android複習旅--XML序列化pull解析

對於XML是什麼,這裡就不多說了,如果不懂可以google學習下。 xml是非常重要和常用的一種資料格式 XML序列化 步驟: 生成一個序列化器 XmlSerializer serializer = Xml.newSerializer(); 要