java并发编程阻塞队列
在前面我们接触的队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了Dequeue接口)。
使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
四.示例和使用场景
下面先使用Object.wait()和Object.notify()、非阻塞队列实现生产者-消费者模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
public class Test { private int queueSize = 10 ; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize); public static void main(String[] args) { Test test = new Test(); Producer producer = test. new Producer(); Consumer consumer = test. new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while ( true ){ synchronized (queue) { while (queue.size() == 0 ){ try { System.out.println( "队列空,等待数据" ); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.poll(); //每次移走队首元素 queue.notify(); System.out.println( "从队列取走一个元素,队列剩余" +queue.size()+ "个元素" ); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while ( true ){ synchronized (queue) { while (queue.size() == queueSize){ try { System.out.println( "队列满,等待有空余空间" ); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notify(); } } queue.offer( 1 ); //每次插入一个元素 queue.notify(); System.out.println( "向队列取中插入一个元素,队列剩余空间:" +(queueSize-queue.size())); } } } } } |
这个是经典的生产者-消费者模式,通过阻塞队列和Object.wait()和Object.notify()实现,wait()和notify()主要用来实现线程间通信。
具体的线程间通信方式(wait和notify的使用)在后续问章中会讲述到。
下面是使用阻塞队列实现的生产者-消费者模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public class Test { private int queueSize = 10 ; private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize); public static void main(String[] args) { Test test = new Test(); Producer producer = test. new Producer(); Consumer consumer = test. new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while ( true ){ try { queue.take(); System.out.println( "从队列取走一个元素,队列剩余" +queue.size()+ "个元素" ); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while ( true ){ try { queue.put( 1 ); System.out.println( "向队列取中插入一个元素,队列剩余空间:" +(queueSize-queue.size())); } catch (InterruptedException e) { e.printStackTrace(); } } } } } |
有没有发现,使用阻塞队列代码要简单得多,不需要再单独考虑同步和线程间通信的问题。
在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。
阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。
java并发编程阻塞队列的更多相关文章
- Java并发编程-阻塞队列(BlockingQueue)的实现原理
背景:总结JUC下面的阻塞队列的实现,很方便写生产者消费者模式. 常用操作方法 常用的实现类 ArrayBlockingQueue DelayQueue LinkedBlockingQueue Pri ...
- Java并发编程——阻塞队列BlockingQueue
Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...
- Java并发编程-阻塞队列
Java concurrent 包中BlockingQueue接口有ArrayBlockingqueue.LinkedBlockingQueue.PriorityBlockingQueue.Synch ...
- java并发:阻塞队列
第一节 阻塞队列 1.1 初识阻塞队列 队列以一种先进先出的方式管理数据,阻塞队列(BlockingQueue)是一个支持两个附加操作的队列,这两个附加的操作是:在队列为空时,获取元素的线程会等待队列 ...
- Java并发编程之队列
Deque(双端队列) 子接口BlockingDeque,实现类如下: ArrayDeque:大下可变的数组双端队列,不允许插入null LinkedList:大小可变的链表双端队列,允许插入null ...
- Java并发编程:阻塞队列(转载)
Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...
- 【转】Java并发编程:阻塞队列
在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList),这些工具都为我们编写多线程程 ...
- 12、Java并发编程:阻塞队列
Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...
- (转)Java并发编程:阻塞队列
Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...
随机推荐
- shell基础笔记1
---恢复内容开始--- 1 test命令中不能使用浮点小数值,如: A=1.444444:[ $A -gt 1 ] 2 test命令中的>或<必须转义,否则shell会把它 ...
- tkinter学习-选择按钮
阅读目录 Checkbutton Radiobutton LabelFrame checkbutton : 说明:多选框控件,用于在程序中提供多项选择框,但是处理“多选一”的问题,还是交给 Radio ...
- java 获取计算机内存
文章来源:https://www.cnblogs.com/hello-tl/p/9341900.html package com.boot.demo.springbootdemo.common.uti ...
- Python9-反射-day27(大年初三)
复习 class 类名(父类,父类2): 静态属性 = '' #静态属性 类属性 def __init__(self): #初始化方法 self.name = 'alex' def func(self ...
- (转)TDD的iOS开发初步以及Kiwi使用入门
本文转自“瞄神”博客 TDD的iOS开发初步以及Kiwi使用入门 测试驱动开发(Test Driven Development,以下简称TDD)是保证代码质量的不二法则,也是先进程序开发的共识.App ...
- 自定义View画一条线
#import "PublishContextView.h" @implementation PublishContextView -(void)drawRect:(CGRect) ...
- Android自动化测试Uiautomator--UiCollection接口简介
这个对象可以理解为一个对象的集合,因为UiSelector描述后得到的有可能是多个满足条件的控件集合,因此可以用来生成UiCollection,继承自UiObject. 用于枚举一个容器的用户界面(U ...
- swift写一个简单的列表unable to dequeue a cell with identifier reuseIdentifier - must register a nib or a cla
报错:unable to dequeue a cell with identifier reuseIdentifier - must register a nib or a class for the ...
- PHP “引号兄弟”
PHP的string最大可以达到2GB,不过很少会用到这么大的字符串. 单引号: 定义一个字符串最简单的方式是使用单引号,而在单引号字符串中要想表达一个单引号,需要在她的前面加个反斜线(\)来进行转义 ...
- HDU 3341 Lost's revenge
Lost's revenge Time Limit: 5000ms Memory Limit: 65535KB This problem will be judged on HDU. Original ...