• 多线程编程:阻塞、并发队列的使用总结


    最近,一直在跟设计的任务调度模块周旋,目前终于完成了第一阶段的调试。今天,我想借助博客园平台把最近在设计过程中,使用队列和集合的一些基础知识给大家总结一下,方便大家以后直接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 }
  • 相关阅读:
    关于32位操作系统和64位操作系统对InstallShield打包的影响
    NEWS: Symantec宣布Wise Package Studio将终止
    InstallShield 2012新功能试用(2) 调用MsiGetProperty等MSI API发生变化
    Basic INFO 在命令行Build InstallShield安装包工程获得压缩安装包
    NEWS InstallShield 2012 Service Pack 1发布
    Basic INFO InstallShield Basic MSI工程中如何在SetupCompleteSuccess界面中启动Readme
    Basic INFO InstallShield的脚本编辑器中如何显示代码行号
    Basic INFO 关于在InstallShield制作的安装包界面中删除InstallShield文字的厂商回复
    Basic INFO InstallShield工程中如何让产品的快捷方式名称始终与产品名保持一致
    Basic INFO: 创建隐藏文件夹
  • 原文地址:https://www.cnblogs.com/1315925303zxz/p/7809843.html
Copyright © 2020-2023  润新知