多线程编程:阻塞、并发队列的使用总结
最近,一直在跟設計的任務調度模塊周旋,目前終于完成了第一階段的調試。今天,我想借助博客園平臺把最近在設計過程中,使用隊列和集合的一些基礎知識給大家總結一下,方便大家以后直接copy。本文都是一些沒有技術含量的東西,只是做個總結,牛哥還請繞路。
老習慣,還是先跟各位紙上談會兒兵,首先說說隊列,他主要分為并發隊列和阻塞隊列,在多線程業務場景中使用最為普遍,我就主要結合我所做過的業務談談我對它們的看法,關于它們的API和官方解釋就不提了。
并發隊列
并發隊列:最常見的業務場景就是多個線程共享同一個隊列中的所有資源,就拿我們公司的業務場景來說,當用戶通過多個渠道下單后,然后就會有多個不同的客戶端通道同時去獲取訂單并處理訂單,為了加快訂單處理速度我們使用并發隊列來充當任務源頭,為了加快處理訂單速度,結合多線程并發來滿足需求。
并發隊列沒什么可說的,就是一個簡單的多線程編程操作,小Demo送給各位:
1 /** 2 * 并發隊列ConcurrentLinkedQueue的使用 3 */ 4 5 public class ConcurrentQueue { 6 7 public static void main(String[] args){ 8 ToyotaYQ yq = new ToyotaYQ(); 9 new Thread(yq,"ToyotaYQ_001").start(); 10 new Thread(yq,"ToyotaYQ_002").start(); 11 new Thread(yq,"ToyotaYQ_003").start(); 12 } 13 14 } 15 16 /** 17 * 任務來源 18 */ 19 class MQ{ 20 private static Queue<String> queue = null; //并發隊列(線程安全) 21 22 /** 23 * 初始化并發隊列 24 */ 25 public static Queue<String> initQueue(){ 26 if(queue == null){ 27 queue = new ConcurrentLinkedQueue<String>(); 28 } 29 String tasklist = "JF1GH78F18G036149,JF1SH95F6AG110830,JF1SJ94D7DG010387,JF1SH92F9CG269249,JF1SH92F5BG215090,JF1SH92F5BG222556,JF1SH92F4CG279994,JF1BR96D7CG114298,JF1BR96D0BG078632,JF1SH95F9AG094011,JF1SH98FXAG186997,JF1BM92D8BG022510,JF1BM92DXAG013855,JF1BM94D8EG036618"; 30 String[] split = tasklist.split(","); 31 List<String> task = Arrays.asList(split); //數組轉集合 32 queue.addAll(task); //按照集合中元素的順序將集合中全部元素放進隊列 33 34 return queue; 35 } 36 } 37 38 /** 39 * 制單客戶端 40 */ 41 class ToyotaYQ implements Runnable{ 42 43 private static final Object lock = new Object(); 44 private static Queue<String> queueYQ = MQ.initQueue(); 45 46 @Override 47 public void run() { 48 while(true){ 49 synchronized (lock){ //盡量減小鎖的粒度和范圍 50 String thisVIN = queueYQ.poll(); 51 if(thisVIN == null){ 52 break; 53 } 54 System.out.println(Thread.currentThread().getName() + "成功制單:" + thisVIN + "。剩余:" + queueYQ.size() + "個任務"); 55 } 56 } 57 } 58 } View Code阻塞隊列
阻塞隊列:最常見的業務場景就是生產者不斷生產任務放進阻塞隊列中,消費者不斷從阻塞隊列中獲取任務;當阻塞隊列中填滿數據時,所有生產者端的線程自動阻塞,當阻塞隊列中數據為空時,所有消費端的線程自動阻塞。這些操作BlockingQueue都已經包辦了,不用我們程序員去操心了。
阻塞隊列我們常用的有:LinkedBlockingQueue和ArrayBlockingQueue,它們在各方面還是很大的區別的;ArrayBlockingQueue在put,take操作使用了同一個鎖,兩者操作不能同時進行,而LinkedBlockingQueue使用了不同的鎖,put操作和take操作可同時進行,以此來提高整個隊列的并發性能。
作為開發者,使用阻塞隊列需要注意的一點是:如果構造一個LinkedBlockingQueue對象,而沒有指定其容量大小,LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),這樣的話,如果生產者的速度一旦大于消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。
阻塞隊列的一些常用方法
下面是我根據這幾天設計的任務調度功能模擬的一個小Demo,只不過項目中使用了MQ服務,這里用阻塞隊列完成可以代替:
1 public class BlockQueueDemo { 2 3 public static void main(String[] args){ 4 BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(2); //定長為2的阻塞隊列 5 //ExecutorService:真正的線程池接口 6 ExecutorService service = Executors.newCachedThreadPool(); //緩存線程池 7 //創建3個生產者: 8 ProducerDemo p1 = new ProducerDemo("車鑒定web端",queue); 9 ProducerDemo p2 = new ProducerDemo("車鑒定APP端",queue); 10 ProducerDemo p3 = new ProducerDemo("車鑒定接口端",queue); 11 ProducerDemo p4 = new ProducerDemo("車鑒定M棧",queue); 12 //創建三個消費者: 13 ConsumerDemo c1 = new ConsumerDemo("ToyotaYQ_001",queue); 14 ConsumerDemo c2 = new ConsumerDemo("ToyotaYQ_002",queue); 15 ConsumerDemo c3 = new ConsumerDemo("ToyotaYQ_003",queue); 16 17 //啟動線程 18 service.execute(p1); 19 service.execute(p2); 20 service.execute(p3); 21 service.execute(p4); 22 service.execute(c1); 23 service.execute(c2); 24 service.execute(c3); 25 26 } 27 } 28 29 /** 30 * 生產者 31 */ 32 class ProducerDemo implements Runnable { 33 private String producerName; 34 private BlockingQueue queue;//阻塞隊列 35 private Random r = new Random(); 36 37 //構造函數,傳入生產者名稱和操作的阻塞隊列 38 public ProducerDemo(String producerName,BlockingQueue queue) { 39 this.producerName = producerName; 40 this.queue = queue; 41 } 42 43 @Override 44 public void run() { 45 while(true){ 46 try { 47 int task = r.nextInt(100); //產生隨機數 48 System.out.println(producerName + "開始生產任務:" + task); 49 queue.put(task); //生產者向隊列中放入一個隨機數 50 Thread.sleep(5000); //減緩生產者生產的速度,如果隊列為空,消費者就會阻塞不會進行消費直到有數據被生產出來 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } 54 } 55 } 56 } 57 58 class ConsumerDemo implements Runnable{ 59 private String consumerName; 60 private BlockingQueue queue;//阻塞隊列 61 62 //構造函數,傳入消費者名稱和操作的阻塞隊列 63 public ConsumerDemo(String consumerName,BlockingQueue queue) { 64 this.consumerName = consumerName; 65 this.queue = queue; 66 } 67 68 @Override 69 public void run() { 70 while(true){ 71 try { 72 System.out.println(consumerName + "開始消費任務---" + queue.take());//消費者從阻塞隊列中消費一個隨機數 73 //Thread.sleep(500); 74 } catch (InterruptedException e) { 75 e.printStackTrace(); 76 } 77 } 78 } 79 }?
開發中各位最常用最熟悉的不過也是集合了,但是前幾天在設計中突然想自己控制任務的分配和修改,這就需要用到靈活操作集合中的內容了,其它也沒什么,但是刪除集合中的元素這一點我們還是必須要很熟練的,雖然是需要借助迭代器來刪除的,但是還是記錄一下吧,方便以后copy。
刪除List集合中的某元素:
1 public class ListDemo { 2 3 public static void main(String[] args){ 4 ArrayList<String> arrList = new ArrayList<String>(); 5 String[] arr = {"一豐","廣豐","寶馬","奧迪","保時捷","沃爾沃","悍馬","路虎","凱迪拉克"}; 6 arrList.addAll(Arrays.asList(arr)); //將數組轉成集合 7 8 //刪除前: 9 for (String thisItem:arrList){ 10 System.out.println("---"+thisItem); 11 } 12 System.out.println("#########################"); 13 14 //使用迭代器刪除集合中的元素 15 Iterator it = arrList.iterator(); 16 while(it.hasNext()){ //it.hasNext()判斷是否還有下一個元素 17 if("悍馬".equals(it.next())){ //it.next()代表下一個元素 18 it.remove(); //【記得:remove()方法一定要調用迭代器的,不能調用List集合的】 19 } 20 } 21 22 //刪除后: 23 for (String thisItem:arrList){ 24 System.out.println("---"+thisItem); 25 } 26 27 } 28 }?
轉載于:https://www.cnblogs.com/1315925303zxz/p/7809843.html
總結
以上是生活随笔為你收集整理的多线程编程:阻塞、并发队列的使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Spring MVC 中 Handle
- 下一篇: Linux搭建Node.js环境