標籤:
BlockingQueue很好的解決了多線程中,如何高效安全“傳輸”資料的問題。通過這些高效並且安全執行緒的隊列類,為我們快速搭建高品質的多線程程式帶來極大的便利。使用情境。
首先它是一個隊列,而一個隊列在資料結構中所起的作用大致如所示:
通過一個共用的隊列,可以使得資料由隊列的一端輸入,從另外一端輸出;在生產者消費者模式中,通過隊列的方式可以很方便的實現兩者之間的資料共用。強大的BlockingQueue使我們不用關心什麼時候需要阻塞線程,什麼時候需要喚醒線程。
BlockingQueue的核心方法:
放入資料:
offer(anObject) 如果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擷取所有可用的資料對象(還可以指定擷取資料的個數),
通過該方法,可以提升擷取資料效率;不需要多次分批加鎖或釋放鎖。
測試代碼:
package BlockingQueue;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class BlockingQueueTest { public static void main(String args[]) throws InterruptedException{ BlockingQueue<String> queue = new ArrayBlockingQueue(10); Producer producer1 = new Producer(queue); Producer producer2 = new Producer(queue); Producer producer3 = new Producer(queue); Consumer consumer = new Consumer(queue); ExecutorService service = Executors.newCachedThreadPool(); service.execute(producer1); service.execute(producer2); service.execute(producer3); service.execute(consumer); Thread.sleep(10 * 1000); producer1.stop(); producer2.stop(); producer3.stop(); Thread.sleep(2000); // 退出Executor service.shutdown(); }}
生產者:
package BlockingQueue;import java.util.Random;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;public class Producer implements Runnable{ private volatile boolean isRunning = true; private BlockingQueue<String> queue; private static AtomicInteger count = new AtomicInteger(); private static final int DEFAULT_RANGE_FOR_SLEEP = 1000; public Producer(BlockingQueue queue){ this.queue = queue; } public void run(){ String data = null; Random r = new Random(); System.out.println("啟動生產者線程"); try{ while(isRunning){ System.out.println("正在生產資料....."); Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP)); data = "data:" + count.incrementAndGet(); System.out.println("將資料:" + data + "放入隊列..."); if (!queue.offer(data, 2, TimeUnit.SECONDS)) { System.out.println("放入資料失敗:" + data); } } }catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally{ System.out.println("退出生產者線程!"); } } public void stop(){ isRunning = false; } }
消費者:
package BlockingQueue;import java.util.Random;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;public class Consumer implements Runnable{ private BlockingQueue<String> queue; private static final int DEFAULT_RANGE_FOR_SLEEP = 1000; public Consumer(BlockingQueue<String> queue){ this.queue = queue; } public void run(){ System.out.println("啟動消費者線程:"); Random r = new Random(); boolean isRunning = true; try{ while(isRunning){ System.out.println("正從隊列擷取資料..."); String data = queue.poll(2,TimeUnit.SECONDS); if(null != data){ System.out.println("拿到資料:" + data); System.out.println("正在消費資料:" + data); Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP)); }else{ isRunning = false; } } }catch(InterruptedException e){ e.printStackTrace(); Thread.currentThread().interrupt(); }finally{ System.out.println("退出消費者線程!"); } }}
參考:http://wsmajunfeng.iteye.com/blog/1629354
使用BlockingQueue的生產者消費者模式