在Java多線程應用中,隊列的使用率很高,多數生產消費模型的首選資料結構就是隊列。Java提供的安全執行緒的Queue可以分為阻塞隊列和非阻塞隊列,其中阻塞隊列的典型例子是BlockingQueue,非阻塞隊列的典型例子是ConcurrentLinkedQueue,在實際應用中要根據實際需要選用阻塞隊列或者非阻塞隊列。
註:什麼叫安全執行緒?這個首先要明確。安全執行緒就是說多線程訪問同一代碼,不會產生不確定的結果。
LinkedBlockingQueue
由於LinkedBlockingQueue實現是安全執行緒的,實現了先進先出等特性,是作為生產者消費者的首選,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的話,預設最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在隊列滿的時候會阻塞直到有隊列成員被消費,take方法在隊列空的時候會阻塞,直到有隊列成員被放進來。
package com.ljq.test;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;public class BlockingQueueTest { /** * * 定義裝蘋果的籃子 * */ public static 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(); } } // 測試方法 public static void testBasket() { // 建立一個裝蘋果的籃子 final Basket basket = new Basket(); // 定義蘋果生產者 class Producer implements Runnable { public String instance = ""; public Producer(String a) { instance = a; } 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 { public String instance = ""; public Consumer(String a) { instance = a; } 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"); } } } ExecutorService service = Executors.newCachedThreadPool(); Producer producer = new Producer("P1"); Producer producer2 = new Producer("P2"); Consumer consumer = new Consumer("C1"); service.submit(producer); service.submit(producer2); service.submit(consumer); // 程式運行5s後,所有任務停止 try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } service.shutdownNow(); } public static void main(String[] args) { BlockingQueueTest.testBasket(); }}
ConcurrentLinkedQueue
ConcurrentLinkedQueue是Queue的一個安全實現.Queue中元素按FIFO原則進行排序.採用CAS操作,來保證元素的一致性。
LinkedBlockingQueue是一個安全執行緒的阻塞隊列,它實現了BlockingQueue介面,BlockingQueue介面繼承自java.util.Queue介面,並在這個介面的基礎上增加了take和put方法,這兩個方法正是隊列操作的阻塞版本。
package com.ljq.test;import java.util.Date;import java.util.Queue;import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentLinkedQueueTest { public static void main(String[] args) { Queue<String> queue = new ConcurrentLinkedQueue<String>(); for (int i = 0; i < 100; i++) { queue.offer(String.valueOf(i)); } int num = 10; // 線程人個數 for (int i = 0; i < num; i++) { new ThreadConn(queue); } } public static class ThreadConn implements Runnable { Queue<String> queue; public ThreadConn(Queue<String> queue) { this.queue = queue; Thread thread = new Thread(this); thread.start(); } public void run() { try { long sd = new Date().getTime(); while (queue.poll() != null) { // 這裡是商務邏輯 System.out.println("1"); } System.out.println(sd); } catch (Exception e) { e.printStackTrace(); } } }}