這一節我們來瞭解阻塞隊列(BlockingQueue),BlockingQueue介面定義了一種阻塞的FIFO queue,每一個BlockingQueue都有一個容量,當容量滿時往BlockingQueue中添加資料時會造成阻塞,當容量為空白時取元素操作會阻塞。首先我們來看ArrayBlockingQueue和LinkedBlockingQueue. ArrayBlockingQueue
ArrayBlockingQueue是一個用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。預設情況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的所有生產者線程或消費者線程,當隊列可用時,可以按照阻塞的先後順序訪問隊列,即先阻塞的生產者線程,可以先往隊列裡插入元素,先阻塞的消費者線程,可以先從隊列裡擷取元素。通常情況下為了保證公平性會降低輸送量。
我們看他的建構函式實現:
//預設是非公平的,初始指定隊列容量public ArrayBlockingQueue(int capacity) { this(capacity, false); }//該構造方法可以設定隊列的公平性。當然如果為公平的,則對效能會產生影響//訪問者的公平性是使用可重新進入鎖實現的public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); }
使用很簡單我們直接看一個執行個體:
public class ProducerConsumerTest { public static void main(String[] args) { final BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(3); ExecutorService service = Executors.newFixedThreadPool(10); for(int i = 0;i<4;i++){ service.execute(new ProducerAndConsumer(blockingQueue)); } }}class ProducerAndConsumer implements Runnable{ private boolean flag = false; private Integer j = 1; private Lock lock = new ReentrantLock(); Condition pro_con = lock.newCondition(); Condition con_con = lock.newCondition(); private BlockingQueue<Integer> blockingQueue; public ProducerAndConsumer(BlockingQueue<Integer> blockingQueue){ this.blockingQueue= blockingQueue; } //生產 public void put(){ try { lock.lock(); while(flag) pro_con.await(); System.out.println("正在準備放入資料。。。"); Thread.sleep(new Random().nextInt(10)*100); Integer value = new Random().nextInt(30); blockingQueue.put(value); System.out.println(Thread.currentThread().getName()+" 放入的資料 "+value); flag = true; con_con.signal(); } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock(); } } public void get(){ try { lock.lock(); while(!flag) con_con.await(); System.out.println("正在準備取資料。。。"); Thread.sleep(new Random().nextInt(10)*1000); System.out.println(Thread.currentThread().getName()+" 取到的資料為"+blockingQueue.take()); flag = false; pro_con.signal(); } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock(); } } @Override public void run() { while(true){ if(j==1){ put(); } else{ get(); } j=(j+1)%2; } }}
輸出為:
正在準備放入資料。。。正在準備放入資料。。。正在準備放入資料。。。正在準備放入資料。。。pool-1-thread-2 放入的資料 13正在準備取資料。。。pool-1-thread-3 放入的資料 4正在準備取資料。。。pool-1-thread-3 取到的資料為13正在準備放入資料。。。pool-1-thread-1 放入的資料 11正在準備取資料。。。pool-1-thread-4 放入的資料 26正在準備取資料。。。pool-1-thread-1 取到的資料為4正在準備放入資料。。。pool-1-thread-2 取到的資料為11正在準備放入資料。。。pool-1-thread-3 放入的資料 18正在準備取資料。。。......
LinkedBlockingQueue
LinkedBlockingQueue是一個用鏈表實現的有界阻塞隊列。此隊列的預設和最大長度為Integer.MAX_VALUE。此隊列按照先進先出的原則對元素進行排序。
先看一下他的建構函式:
public LinkedBlockingQueue() { this(Integer.MAX_VALUE); //MAX_VALUE=2147483647 }public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node<E>(null); }
我們還是直接開看一個例子:
public class BlockingQueueTest { /** * 定義裝蘋果的籃子 */ public static class Basket { // 籃子,能夠容納3個蘋果 // BlockingQueue<String> basket = new ArrayBlockingQueue<String>(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 { // get方法取出一個蘋果,若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) { } } } // 定義蘋果消費者 class Consumer implements Runnable { public String instance = ""; public Consumer(String a) { instance = a; } public void run() { try { while (true) { // 消費蘋果 System.out.println("消費者準備消費蘋果:" + instance); basket.consume(); System.out.println("! 消費者消費蘋果完畢:" + instance); // 休眠1000ms Thread.sleep(1000); } } catch (InterruptedException ex) { } } } 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); // 程式運行3s後,所有任務停止 try { Thread.sleep(3000); } catch (InterruptedException e) { } service.shutdownNow(); } public static void main(String[] args) { BlockingQueueTest.testBasket(); }}
輸出為:
生產者準備生產蘋果:P1消費者準備消費蘋果:C1! 生產者生產蘋果完畢:P1生產者準備生產蘋果:P2! 消費者消費蘋果完畢:C1! 生產者生產蘋果完畢:P2生產者準備生產蘋果:P2! 生產者生產蘋果完畢:P2生產者準備生產蘋果:P1! 生產者生產蘋果完畢:P1生產者準備生產蘋果:P2生產者準備生產蘋果:P1消費者準備消費蘋果:C1! 消費者消費蘋果完畢:C1! 生產者生產蘋果完畢:P2生產者準備生產蘋果:P2消費者準備消費蘋果:C1! 消費者消費蘋果完畢:C1! 生產者生產蘋果完畢:P1生產者準備生產蘋果:P1消費者準備消費蘋果:C1! 消費者消費蘋果完畢:C1! 生產者生產蘋果完畢:P2Process finished with exit code 0