1. 程式人生 > >淺談SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue

淺談SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue

分析這幾個阻塞佇列是自己在分析執行緒池的時候,發現Executors類提供的幾個靜態方法中,newCachedThreadPool()的阻塞佇列使用的

是SynchronousQueue,而剩下兩個用的是LinkedBlockingQueue,好奇為什麼不同,於是便研究了一下,寫下自己的理解:

在研究過程中,發現SynchronousQueue特別有意思,首先一些個人對這個類的理解,感覺這個同步佇列好好玩,裡面不存能東西,放不進去

也拿不出來。它的同步原理是,當生成者執行緒準備將元素放入這個佇列時,如果這時沒有消費者執行緒來,它就一直wait,等到有消費者執行緒來

了,消費者執行緒把元素取走,他就可以返回true了。同樣的,當消費者準備從這個佇列取元素時,如果這時正好有生產者執行緒來時,他就把元

素取走返回true,否則就一直在那等生產者執行緒來。下面來段正式的:

SynchronousQueue是無界的,是一種無緩衝的等待佇列。它的方法返回值特有意思:

isEmpty()方法永遠返回是true,remainingCapacity() 方法永遠返回是0,remove()和removeAll() 方法永遠返回是false,

iterator()方法永遠返回空,peek()方法永遠返回null。它非常適合於傳遞性設計,在這種設計中,在一個執行緒中執行的物件要

將某些資訊、事件或任務傳遞給在另一個執行緒中執行的物件,它就必須與該物件同步。對於正在等待的生產者和使用者執行緒而言,此類支援可

選的公平排序策略。預設情況下不保證這種排序(即預設非公平)。但是,使用公平設定為 true 所構造的佇列可保證執行緒以 FIFO 的順序

進行訪問。 公平通常會降低吞吐量,但是可以減小可變性並避免得不到服務(比如執行緒出現飢渴現象)。

         注意1:它一種阻塞佇列,其中每個 put 必須等待一個 take,反之亦然。同步佇列沒有任何內部容量,甚至連一個佇列的容量都沒有。

注意2:它是執行緒安全的,是阻塞的。
 注意3:不允許使用 null 元素。
 注意4:公平排序策略是指呼叫put的執行緒之間,或take的執行緒之間。

LinkedBlockingQueue:

 LinkedBlockingQueue是無界的,是一個無界快取的等待佇列。

        基於連結串列的阻塞佇列,內部維持著一個數據緩衝佇列(該佇列由連結串列構成)。當生產者往佇列中放入一個數據時,佇列會從生產者手中獲取資料,並快取在佇列內部,而生產者立即返回;只有當佇列緩衝區達到最大值快取容量時(LinkedBlockingQueue可以通過建構函式指定該值),才會阻塞生產者佇列,直到消費者從佇列中消費掉一份資料,生產者執行緒會被喚醒,反之對於消費者這端的處理也基於同樣的原理。

LinkedBlockingQueue之所以能夠高效的處理併發資料,還因為其對於生產者端和消費者端分別採用了獨立的鎖來控制資料同步,這也意味著在高併發的情況下生產者和消費者可以並行地操作佇列中的資料,以此來提高整個佇列的併發效能。

ArrayBlockingQueue:

    ArrayListBlockingQueue是有界的,是一個有界快取的等待佇列。
         基於陣列的阻塞佇列,同LinkedBlockingQueue類似,內部維持著一個定長資料緩衝佇列(該佇列由陣列構成)。ArrayBlockingQueue內部還儲存著兩個整形變數,分別標識著佇列的頭部和尾部在陣列中的位置。
         ArrayBlockingQueue在生產者放入資料和消費者獲取資料,都是共用同一個鎖物件,由此也意味著兩者無法真正並行執行,這點尤其不同於LinkedBlockingQueue;按照實現原理來分析,ArrayBlockingQueue完全可以採用分離鎖,從而實現生產者和消費者操作的完全並行執行。Doug Lea之所以沒這樣去做,也許是因為ArrayBlockingQueue的資料寫入和獲取操作已經足夠輕巧,以至於引入獨立的鎖機制,除了給程式碼帶來額外的複雜性外,其在效能上完全佔不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue間還有一個明顯的不同之處在於,前者在插入或刪除元素時不會產生或銷燬任何額外的物件例項,而後者則會生成一個額外的Node物件。這在長時間內需要高效併發地處理大批量資料的系統中,其對於GC的影響還是存在一定的區別。

         ArrayBlockingQueue和LinkedBlockingQueue是兩個最普通、最常用的阻塞佇列,一般情況下,處理多執行緒間的生產者消費者問題,使用這兩個類足以。

分析完這三個,回到最初的問題,為什麼newCachedThreadPool()用的是SynchronousQueue。

當初始maximumPool為空,或者maximumPool中當前沒有空閒執行緒時,將沒有執行緒執行Synchronize.poll(),這將失敗,此時

CachedThreadPool會建立一個新的執行緒執行任務execute()方法執行任務。

參考文章:http://blog.csdn.net/hudashi/article/details/7076814

http://blog.csdn.net/mn11201117/article/details/8671497#java