zoukankan      html  css  js  c++  java
  • 多线程编程:阻塞、并发队列的使用总结

    最近,一直在跟设计的任务调度模块周旋,目前终于完成了第一阶段的调试。今天,我想借助博客园平台把最近在设计过程中,使用队列和集合的一些基础知识给大家总结一下,方便大家以后直接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都已经包办了,不用我们程序员去操心了。

    阻塞队列我们常用的有:LinkedBlockingQueueArrayBlockingQueue,它们在各方面还是很大的区别的;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 }
  • 相关阅读:
    【转】Intel Atom手机处理器“上窜下跳”
    神奇HVXC的MOS 分
    Skype 将支持 WebRTC 标准
    一篇文章算市值
    算法经典趣题三色旗
    Java基础(2)Java三大版本/体系
    一天一个 Linux 命令(27):mkfs 命令
    Java基础(1)Java特性及优势
    一天一个 Linux 命令(26):fdisk 命令
    关于Java中的整数类型值比较的疑问
  • 原文地址:https://www.cnblogs.com/1315925303zxz/p/7809843.html
Copyright © 2011-2022 走看看