標籤:java synchronousqueue 阻塞隊列 同步隊列
在BlockingQueue的子類中有一個SynchronousQueue(同步隊列)比較少見,現在做一個簡單的介紹,並附加一個簡單的例子。SynchronousQueue --JDK1.6介紹:
-
public class SynchronousQueue<E>
-
extends AbstractQueue<E>
-
implements BlockingQueue<E>, Serializable
一種阻塞隊列,其中每個插入操作必須等待另一個線程的對應移除操作 ,反之亦然。同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。不能在同步隊列上進行 peek,因為僅在試圖要移除元素時,該元素才存在;除非另一個線程試圖移除某個元素,否則也不能(使用任何方法)插入元素;也不能迭代隊列,因為其中沒有元素可用於迭代。隊列的頭 是嘗試添加到隊列中的首個已排隊插入線程的元素;如果沒有這樣的已排隊線程,則沒有可用於移除的元素並且 poll() 將會返回 null。對於其他 Collection 方法(例如 contains),SynchronousQueue 作為一個空 collection。此隊列不允許 null 元素。
同步隊列類似於 CSP 和 Ada 中使用的 rendezvous 通道。它非常適合於傳遞性設計,在這種設計中,在一個線程中啟動並執行對象要將某些資訊、事件或任務傳遞給在另一個線程中啟動並執行對象,它就必須與該對象同步。
對於正在等待的生產者和使用者線程而言,此類支援可選的公平排序策略。預設情況下不保證這種排序。但是,使用公平設定為 true 所構造的隊列可保證線程以 FIFO 的順序進行訪問。公平通常會降低輸送量,但是可以減小可變性並避免得不到服務。
此類及其迭代器實現 Collection
和 Iterator
介面的所有可選 方法。
簡介及注意點
SynchronousQueue同步隊列(哈哈不知準確否)繼承了BlockingQueue<E>介面,功能類似於:一直等待,來一個及時處理一個,但不能同事處理兩個。比如queue.take()方法會阻塞,一直queue.offer(element)插入一個元素,二插入的元素馬上就別queue.take()處理掉。所以它沒有容納元素的能力,isEmpty方法總是返回true,但是給人的感覺像是可以臨時容納一個元素。
另外在建立SynchronousQueue時可以傳遞一個boolean參數來指定它是否是訪問它的線程按遵守FIFO順序處理,true表示遵守FIFO。
注意: 注意1:它一種阻塞隊列,其中每個 put 必須等待一個 take,反之亦然。
同步隊列沒有任何內部容量,甚至連一個隊列的容量都沒有。
注意2:它是安全執行緒的,是阻塞的。
注意3: 不允許使用 null 元素。
注意4:公平排序策略是指調用put的線程之間,或take的線程之間。 公平排序策略可以查考ArrayBlockingQueue中的公平策略。
注意5:SynchronousQueue的以下方法很有趣:
* iterator() 永遠返回空,因為裡面沒東西。
* peek() 永遠返回null。
* put() 往queue放進去一個element以後就一直wait直到有其他thread進來把這個element取走。
* offer() 往queue裡放一個element後立即返回,如果碰巧這個element被另一個thread取走了,offer方法返回true,認為offer成功;否則返回false。
* offer(2000, TimeUnit.SECONDS) 往queue裡放一個element但是等待指定的時間後才返回,返回的邏輯和offer()方法一樣。
* take() 取出並且remove掉queue裡的element(認為是在queue裡的。。。),取不到東西他會一直等。
* poll() 取出並且remove掉queue裡的element(認為是在queue裡的。。。),只有到碰巧另外一個線程正在往queue裡offer資料或者put資料的時候,該方法才會取到東西。否則立即返回null。
* poll(2000, TimeUnit.SECONDS) 等待指定的時間然後取出並且remove掉queue裡的element,其實就是再等其他的thread來往裡塞。
* isEmpty()永遠是true。
* remainingCapacity() 永遠是0。
* remove()和removeAll() 永遠是false。程式碼範例:
import java.util.Random;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;/** * 下面使用SynchronousQueue類比: * 最多隻有一個產品的生產者-消費者模型 * * 消費者線程們 逐個請求消費產品 * 生產者線程們 逐個生產產品。 * * @author maguowei01 * */public class SynchronousQueueTest {public static void main(String[] args) { //true保證生產或消費者線程以FIFO的順序訪問。SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>(true);for (int i = 0; i < 3; ++i) {new Customer(queue).start();}for (int i = 0; i < 3; ++i) {new Product(queue).start();}}static class Product extends Thread {SynchronousQueue<Integer> queue;public Product(SynchronousQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {int rand = new Random().nextInt(1000);System.out.println("Thread Id:" + getId() + " 生產了一個產品:" + rand);System.out.println("Thread Id:" + getId() + " 等待兩秒後運送出去...");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}/* * offer()往queue裡放一個element後立即返回,如果碰巧這個element被另一個thread取走了, * offer方法返回true,認為offer成功;否則返回false。 * 也就是說offer不一定真正的插入的隊列中,肯定沒成功丟失了 */// queue.offer(rand); //注意offer與put方法的區別try {/* * put()往queue放進去一個element以後就一直wait直到有其他thread進來把這個element取走。 */queue.put(rand);} catch (InterruptedException e) {e.printStackTrace();}}}}static class Customer extends Thread {SynchronousQueue<Integer> queue;public Customer(SynchronousQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {// 線程運行到queue.take()阻塞,直到Product生產一個產品queue.offer。System.out.println("Thread Id:" + getId() + " 消費了一個產品:" + queue.take());} catch (InterruptedException e) {e.printStackTrace();}System.out.println("------------------------------------------");}}}}