簡析SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue
SynchronousQueue
SynchronousQueue是無界的,是一種無緩衝的等待佇列,但是由於該Queue本身的特性,在某次新增元素後必須等待其他執行緒取走後才能繼續新增;可以認為SynchronousQueue是一個快取值為1的阻塞佇列,但是 isEmpty()方法永遠返回是true,remainingCapacity() 方法永遠返回是0,remove()和removeAll() 方法永遠返回是false,iterator()方法永遠返回空,peek()方法永遠返回null。
宣告一個SynchronousQueue有兩種不同的方式,它們之間有著不太一樣的行為。公平模式和非公平模式的區別:如果採用公平模式:SynchronousQueue會採用公平鎖,並配合一個FIFO佇列來阻塞多餘的生產者和消費者,從而體系整體的公平策略;但如果是非公平模式(SynchronousQueue預設):SynchronousQueue採用非公平鎖,同時配合一個LIFO佇列來管理多餘的生產者和消費者,而後一種模式,如果生產者和消費者的處理速度有差距,則很容易出現飢渴的情況,即可能有某些生產者或者是消費者的資料永遠都得不到處理。
LinkedBlockingQueue
LinkedBlockingQueue是無界的,是一個無界快取的等待佇列。
基於連結串列的阻塞佇列,內部維持著一個數據緩衝佇列(該佇列由連結串列構成)。當生產者往佇列中放入一個數據時,佇列會從生產者手中獲取資料,並快取在佇列內部,而生產者立即返回;只有當佇列緩衝區達到最大值快取容量時(LinkedBlockingQueue可以通過建構函式指定該值),才會阻塞生產者佇列,直到消費者從佇列中消費掉一份資料,生產者執行緒會被喚醒,反之對於消費者這端的處理也基於同樣的原理。
LinkedBlockingQueue之所以能夠高效的處理併發資料,還因為其對於生產者端和消費者端分別採用了獨立的鎖來控制資料同步,這也意味著在高併發的情況下生產者和消費者可以並行地操作佇列中的資料,以此來提高整個佇列的併發效能。
ArrayListBlockingQueue
ArrayListBlockingQueue是有界的,是一個有界快取的等待佇列。
基於陣列的阻塞佇列,同LinkedBlockingQueue類似,內部維持著一個定長資料緩衝佇列(該佇列由陣列構成)。ArrayBlockingQueue內部還儲存著兩個整形變數,分別標識著佇列的頭部和尾部在陣列中的位置。
ArrayBlockingQueue在生產者放入資料和消費者獲取資料,都是共用同一個鎖物件,由此也意味著兩者無法真正並行執行,這點尤其不同於LinkedBlockingQueue;按照實現原理來分析,ArrayBlockingQueue完全可以採用分離鎖,從而實現生產者和消費者操作的完全並行執行。Doug Lea之所以沒這樣去做,也許是因為ArrayBlockingQueue的資料寫入和獲取操作已經足夠輕巧,以至於引入獨立的鎖機制,除了給程式碼帶來額外的複雜性外,其在效能上完全佔不到任何便宜。
ArrayBlockingQueue和LinkedBlockingQueue間還有一個明顯的不同之處在於,前者在插入或刪除元素時不會產生或銷燬任何額外的物件例項,而後者則會生成一個額外的Node物件。這在長時間內需要高效併發地處理大批量資料的系統中,其對於GC的影響還是存在一定的區別。
ArrayBlockingQueue和LinkedBlockingQueue是兩個最普通、最常用的阻塞佇列,一般情況下,處理多執行緒間的生產者消費者問題,使用這兩個類足以。