10____java新特性之隊列(blockingQueue)

blockingQueue(阻塞隊列)在java併發庫包中,很好的解決了消費者和生產者結構的模式的問題(隊列)。

假設我們有若干生產者線程,另外又有若干個消費者線程。如果生產者線程需要把準備好的數據共享給消費者線程,利用隊列的方式來傳遞數據,就可以很方便地解決他們之間的數據共享問題。當隊列中填滿數據的情況下,生產者端的所有線程都會被自動阻塞,直到隊列中有空的位置,線程被自動喚醒。我們不用關心什麼時候需要阻塞線程,什麼時候喚醒線程,這一切都由blockingQueue自動完成。


隊列模式主要有兩種:

先進先出(FIFO):先插入的隊列的元素也最先出隊列,類似於排隊的功能。從某種程度上來說這種隊列也體現了一種公平性。
後進先出(LIFO):後插入隊列的元素最先出隊列,這種隊列優先處理最近發生的事件。


BlockingQueue的具體實現有以下幾類:

ArrayBlockingQueue:
//數組阻塞隊列,內部定義了一個緩存數組,還有兩個變量,分別標識隊列頭部和尾部的位置

LinkedBlockingQueue:
//基於鏈表的阻塞隊列,內部定義了一個鏈表式的緩存隊列。
//我們需要注意的是,如果構造一個LinkedBlockingQueue對象,而沒有指定其容量大小,LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),
//這樣的話,如果生產者的速度一旦大於消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。

DelayQueue:
//DelayQueue中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue是一個沒有大小限制的隊列,因此往隊列中插入數據的操作(生產者)
//永遠不會被阻塞,而只有獲取數據的操作(消費者)纔會被阻塞。

PriorityBlockingQueue:
//基於優先級的阻塞隊列(優先級的判斷通過構造函數傳入的Compator對象來決定),但需要注意的是PriorityBlockingQueue並不會阻塞數據生產者,
//而只會在沒有可消費的數據時,阻塞數據的消費者。因此使用的時候要特別注意,生產者生產數據的速度絕對不能快於消費者消費數據的速度,
//否則時間一長,會最終耗盡所有的可用堆內存空間。

SynchronousQueue:
一種無緩衝的等待隊列,消費者和生產者直接交易。 

這五種具體實現,經常使用的只有ArrayBlockingQueue和LinkedBlockingQueue。


BlockingQueue的方法主要有:

放入數據:

offer(anObject):
//表示如果可能的話,將anObject加到BlockingQueue裏,即如果BlockingQueue可以容納,則返回true,否則返回false.(本方法不阻塞當前執行方法的線程)

offer(E o, long timeout, TimeUnit unit):
//可以設定等待的時間,如果在指定的時間內,還不能往隊列中加入BlockingQueue,則返回失敗。

put(anObject):
//把anObject加到BlockingQueue裏,如果BlockQueue沒有空間,則調用此方法的線程被阻斷直到BlockingQueue裏面有空間再繼續.
獲取數據:
poll(time):
//取走BlockingQueue裏排在首位的對象,若不能立即取出,則可以等time參數規定的時間,取不到時返回null;

poll(long timeout, TimeUnit unit):
//從BlockingQueue取出一個隊首的對象,如果在指定時間內,隊列一旦有數據可取,則立即返回隊列中的數據。否則知道時間超時還沒有數據可取,返回失敗。

take():
//取走BlockingQueue裏排在首位的對象,若BlockingQueue爲空,阻斷進入等待狀態直到BlockingQueue有新的數據被加入;

drainTo():
//一次性從BlockingQueue獲取所有可用的數據對象(還可以指定獲取數據的個數),通過該方法,可以提升獲取數據效率;不需要多次分批加鎖或釋放鎖。



ArrayBlockingQueue:

public class BlockingQueueTest {
	public static void main(String[] args) {
		final BlockingQueue queue = new ArrayBlockingQueue(3);
		for(int i=0;i<2;i++){	
			new Thread(){
				public void run(){
					while(true){
						try {
							Thread.sleep((long)(Math.random()*1000));
							System.out.println(Thread.currentThread().getName() + "準備放數據!");
							queue.put(1);
							System.out.println(Thread.currentThread().getName() + "已經放了數據," +
									"隊列目前有" + queue.size() + "個數據");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}.start();	
}

		new Thread(){
			public void run(){
				while(true){
					try {
						//將此處的睡眠時間分別改爲100和1000,觀察運行結果
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getName() + "準備取數據!");
						queue.take();
						System.out.println(Thread.currentThread().getName() + "已經取走數據," +
								"隊列目前有" + queue.size() + "個數據");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
} 

打印結果:

Thread-0準備放數據!
Thread-0已經放了數據,隊列目前有1個數據
Thread-1準備放數據!
Thread-1已經放了數據,隊列目前有2個數據
Thread-0準備放數據!
Thread-0已經放了數據,隊列目前有3個數據
Thread-2準備取數據!
Thread-2已經取走數據,隊列目前有2個數據
Thread-1準備放數據!
Thread-1已經放了數據,隊列目前有3個數據
Thread-1準備放數據!
Thread-0準備放數據!
Thread-2準備取數據!
Thread-2已經取走數據,隊列目前有2個數據
Thread-1已經放了數據,隊列目前有3個數據


LinkBlockingQueue:

public class BlockingQueueTest2 {
    /**
     * 
     * 定義裝蘋果的籃子
     * 
     */
    public class Basket {
        // 籃子,能夠容納3個蘋果
        BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);

        // 生產蘋果,放入籃子
        public void produce() throws InterruptedException {
            // put方法放入一個蘋果,若basket滿了,等到basket有位置
            basket.put("An apple");
        }

        // 消費蘋果,從籃子中取走
        public String consume() throws InterruptedException {
            // take方法取出一個蘋果,若basket爲空,等到basket有蘋果爲止(獲取並移除此隊列的頭部)
            return basket.take();
        }
    }

    // 定義蘋果生產者
    class Producer implements Runnable {
        private String instance;
        private Basket basket;

        public Producer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 生產蘋果
                    System.out.println("生產者準備生產蘋果:" + instance);
                    basket.produce();
                    System.out.println("!生產者生產蘋果完畢:" + instance);
                    // 休眠300ms
                    Thread.sleep(300);
                }
            } catch (InterruptedException ex) {
                System.out.println("Producer Interrupted");
            }
        }
    }

    // 定義蘋果消費者
    class Consumer implements Runnable {
        private String instance;
        private Basket basket;

        public Consumer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 消費蘋果
                    System.out.println("消費者準備消費蘋果:" + instance);
                    System.out.println(basket.consume());
                    System.out.println("!消費者消費蘋果完畢:" + instance);
                    // 休眠1000ms
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {
                System.out.println("Consumer Interrupted");
            }
        }
    }

    public static void main(String[] args) {
        BlockingQueueTest2 test = new BlockingQueueTest2();

        // 建立一個裝蘋果的籃子
        Basket basket = test.new Basket();

        ExecutorService service = Executors.newCachedThreadPool();
        Producer producer = test.new Producer("生產者001", basket);
        Producer producer2 = test.new Producer("生產者002", basket);
        Consumer consumer = test.new Consumer("消費者001", basket);
        service.submit(producer);
        service.submit(producer2);
        service.submit(consumer);
        // 程序運行5s後,所有任務停止
//        try {
//            Thread.sleep(1000 * 5);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        service.shutdownNow();
    }

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章