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

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

阻塞队列

阻塞队列:最常见的业务场景就是生产者不断生产任务放进阻塞队列中,消费者不断从阻塞队列中获取任务;当阻塞队列中填满数据时,所有生产者端的线程自动阻塞,当阻塞队列中数据为空时,所有消费端的线程自动阻塞。这些操作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 }
时间: 2024-11-11 23:00:40

多线程编程:阻塞、并发队列的使用总结的相关文章

多线程编程-之并发编程:阻塞队列

在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList),这些工具都为我们编写多线程程序提供了很大的方便.今天我们来讨论另外一类容器:阻塞队列. 在前面我们接触的队列都是非阻塞队列,比如PriorityQueue.LinkedList(LinkedList是双向链表,它实现了Dequeue接口). 使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者

多线程编程-之并发编程:同步容器

为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer. 同时说说List,Set,Map之间的区别. 自动扩展的数组: List 重复的数组:set 自动排序的组数 :TreeSet,TreeList,Tree** 而几乎所有的集合都是基于数组来实现的. 因为集合是对数组做的封装,所以,数组永远比任何一个集合要快 但任何一个集合,比数组提供的功能要多 一:数组声明了它容纳的元素的类型,而集合不声明.这是由于集合以o

{objccn.io}学习笔记-并发编程-底层并发API

1.不要在GCD中干『持久』的事 GCD 通过创建所谓的线程池来大致匹配 CPU 内核数量.要记住,线程的创建并不是无代价的.每个线程都需要占用内存和内核资源.这里也有一个问题:如果你提交了一个 block 给 GCD,但是这段代码阻塞了这个线程,那么这个线程在这段时间内就不能用来完成其他工作——它被阻塞了.为了确保功能点在队列上一直是执行的,GCD 不得不创建一个新的线程,并把它添加到线程池. 如果你的代码阻塞了许多线程,这会带来很大的问题.首先,线程消耗资源,此外,创建线程会变得代价高昂.创

并发编程—— 阻塞队列和生产者-消费者模式

Java并发编程实践 目录 并发编程—— ConcurrentHashMap 并发编程—— 阻塞队列和生产者-消费者模式 概述 第1部分 为什么要使用生产者和消费者模式 第2部分 什么是生产者消费者模式 第3部分 代码示例 第1部分 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据.同样的道理,如果消费者的处理能力大于生产者,那么消费

多线程编程学习六(Java 中的阻塞队列).

介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取元素的容器. 当线程 插入/获取 动作由于队列 满/空 阻塞后,队列也提供了一些机制去处理,或抛出异常,或返回特殊值,或者线程一直等待... 方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 插入方法 add(e) offer(e) put(e) offer(e, timeout, unit

Python 3 并发编程多进程之队列(推荐使用)

Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往队列里放任意类型的数据 创建队列的类(底层就是以管道和锁定的方式实现): 1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. 参数介绍: 1 maxsize是队列中允许最大项数,省略则无大小限制. 方法介绍: 1.主要

并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法(转)

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出).Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列. 注:什么叫线程安全?这个首先要明确.线程安全就是说多线程访问同一代码,不会产生不确定的结果. 并行和并发区别 1.并行是指两者同时执行一件事,比如赛跑,两个人都在不停的

阻塞队列和并发队列

在并发编程中我们有时候需要使用线程安全的队列.要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法. 使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,其中阻塞队列的典型是:BlockingQueue; 非阻塞的实现方式则可以使用循环CAS的方式来实现,非阻塞队列的典型例子是ConcurrentLinkedQueue. 注:并行与并发的区别: 1.并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑: 2.并

多线程编程学习笔记——使用并发集合(三)

接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 四.   使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费任务的工作者间如何扩展工作量. 1.程序代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sy