Queue是一个队列,而队列的主要特征是FIFO先进先出,要实现生产者与消费者模型,也可以采用队列来进行中间的缓冲读取,好处是:生产者可以一直不停歇的生产数据。

BlockingQueue是Queue的子类,它实现有队列的基本特征:

public interface BlockingQueue<E> extends Queue<E>

在最初利用Queue实现生产者与消费者模型的时候发现一个问题:所有的消费者可能不是一个轮流操作,而是有可能某一个消费者长期进行消费处理。

阻塞队列

BlockingQueue通常用于一个线程生成对象,而另外一个线程消费这些对象的场景。

  一个线程将会持续生成新对象并将其插入到队列之中,直到队列达到它所能容纳的临界点,也就是说,它是有限的。如果该阻塞队列到达了临界点,负责生产的线程将会再往里面插入新对象时发生阻塞。它会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。

BlockingQueue也是一个处理接口, 如果要想操作BlockingQueue也需要使用它的一系列子类:

对于阻塞队列而言最基础的两个实现子类:数组的队列,链表的队列。

范例:使用BlockingQueue实现一个消费者与生产者模型

package so.strong.mall.concurrent;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit; public class QueueDemo {
public static void main(String[] args) {
final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(5);//允许保存5个数据队列
for (int i = 0; i < 3; i++) { //3个生产者
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
try {
TimeUnit.SECONDS.sleep(j);
String str = "[生产数据{" + Thread.currentThread().getName() + "}] j = " + j;
queue.put(str); //会进入到生产的阻塞状态
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "生产者-" + i).start();
} for (int i = 0; i < 5; i++) { //5个消费者
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
if (queue.isEmpty()) { //队里内容为空
break;
}
System.out.println("[消费数据{" + Thread.currentThread().getName() + "}]" + queue.take());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "消费者-" + i).start(); }
}
}

除了数组之外也可以使用链表来进行操作:LinkedBlockingQueue。在使用这个类进行BlockingQueue接口对象实例化的时候,如果没有设置容量,则容量为:Integer.MAX_VALUE。

范例:修改为链表实现消费者与生产者模型

final BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);//允许保存5个数据队列

链表是通过索引在进行弹出数据的,而链表只需要弹出第一个元素即可。

范例:采用优先级的PriorityBlockingQueue来实现数据操作

public class PriorityBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable

PriorityBlockingQueue利用了Comparable接口来进行处理完成。

final BlockingQueue<String> queue = new PriorityBlockingQueue<>(5);//允许保存5个数据队列

对于使用哪一种具体的子类完全是由具体的开发环境来决定的。需要至少知道BlockingQueue这个阻塞队列核心就是提供有同步队列的功能。

SychronousQueue同步队列

之前使用的BlockingQueue每一次都可以保存多个数据对象信息,但是有些时候只能够允许保存一个数据的信息,这种情况下就要使用SychronousQueue子类来完成、

范例:使用同步队列来进行处理

package so.strong.mall.concurrent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit; public class QueueDemo {
public static void main(String[] args) {
final BlockingQueue<String> queue = new SynchronousQueue<>();//允许保存5个数据队列
for (int i = 0; i < 3; i++) { //3个生产者
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
try {
TimeUnit.SECONDS.sleep(j);
String str = "[生产数据{" + Thread.currentThread().getName() + "}] j = " + j;
queue.put(str); //会进入到生产的阻塞状态
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "生产者-" + i).start();
} for (int i = 0; i < 5; i++) { //5个消费者
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("[消费数据{" + Thread.currentThread().getName() + "}]" + queue.take());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "消费者-" + i).start();
}
}
}

现在不关心有多少个消费者与生产者,都采用一个接一个的形式执行。

BlockingDeque双端阻塞队列

BlockingQueue主要特征是只能够从一方面获取数据,也就是说它按照一个队列的形式采用了FIFO处理完成。但是现在希望可以按照前后各自处理,那么就需要BlocingDeque。

一个BlockingDeque线程在双端队列的两端都可以插入数据和提取数据。

一个线程生产元素,并把它们插入到队列的任意一端。如果双端队列已满,插入线程将被阻塞,直到一个移除线程从该队列中移除了一个元素。如果双端队列为空,移除线程将被阻塞,直到一个插入线程向该队列插入了一个新元素。

BlockingDeque接口结构:

public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E>

子类LinkedBlockingDeque类结构

public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>,  java.io.Serializable

范例:观察双端队列的基本使用

package so.strong.mall.concurrent;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit; /**
* @author Termis
* @date 2018/5/21
*/
public class QueueDemo {
public static void main(String[] args) {
final BlockingDeque<String> deque = new LinkedBlockingDeque<>();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
try {
TimeUnit.SECONDS.sleep(1);
String str;
if (j % 2 == 0) {
str = "[FIRST]生产数据{" + Thread.currentThread().getName() + "}y=" + j;
deque.addFirst(str);
} else {
str = "[LAST]生产数据{" + Thread.currentThread().getName() + "}y=" + j;
deque.addLast(str);
}
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "生产者-" + i).start();
} new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("[First]消费数据{" + Thread.currentThread().getName() + "}" + deque.takeFirst());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "消费者First").start(); new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("[Last]消费数据{" + Thread.currentThread().getName() + "}" + deque.takeLast());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, "消费者Last").start(); }
}

生产者和消费者模型的实现方案很多。对于双端队列一定清楚它本身还是一个队列。如果现在first已经拽干净了,那么就继续拽last,就会有可能出现first消费last的情况。

public class QueueDemo {
public static void main(String[] args) throws Exception {
BlockingDeque<String> deque = new LinkedBlockingDeque<>();
deque.addFirst("hello-first");
deque.addFirst("world-first");
deque.addLast("hello-last");
deque.addLast("world-last");
System.out.println(deque.takeFirst());
System.out.println(deque.takeFirst());
System.out.println(deque.takeFirst());
System.out.println(deque.takeFirst());
}
}
world-first
hello-first
hello-last
world-last

如果一端出现了阻塞,那么至少另外一端可以继续使用。

JUC——阻塞队列的更多相关文章

  1. java高并发系列 - 第25天:掌握JUC中的阻塞队列

    这是java高并发系列第25篇文章. 环境:jdk1.8. 本文内容 掌握Queue.BlockingQueue接口中常用的方法 介绍6中阻塞队列,及相关场景示例 重点掌握4种常用的阻塞队列 Queu ...

  2. java并发编程工具类JUC第一篇:BlockingQueue阻塞队列

    Java BlockingQueue接口java.util.concurrent.BlockingQueue表示一个可以存取元素,并且线程安全的队列.换句话说,当多线程同时从 JavaBlocking ...

  3. JUC 并发编程--07 阻塞队列版本的 生产者消费者(不使用synchronized和 lock),也有一些疑惑,最终解惑

    直接上代码: 前提是你已经 熟悉了原子类,volatile,和阻塞队列 public class JucPCdemo03 { /** * 阻塞队列的应用: 这里实现的生产者消费者,生产一个消费一个 * ...

  4. JUC之Java中的阻塞队列及其实现原理

    在文章线程池实现原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线程池的组成部分,其中一个组成部分就是阻塞队列.那么JAVA中的阻塞队列如何实现的呢? 阻塞队列,关键字是阻塞 ...

  5. JUC组件扩展(三):BlockingQueue(阻塞队列)详解

    一. 前言 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大 ...

  6. 【JUC】阻塞队列&生产者和消费者

    阻塞队列 线程1往阻塞队列添加元素[生产者] 线程2从阻塞队列取出元素[消费者] 当队列空时,获取元素的操作会被阻塞 当队列满时,添加元素的操作会被阻塞 阻塞队列的优势:在多线程领域,发生阻塞时,线程 ...

  7. JUC 并发编程--10, 阻塞队列之--LinkedBlockingDeque 工作窃取, 代码演示

    直接上代码 class LinkedBlockingDequeDemo { // 循环是否结束的开关 private static volatile boolean flag1 = true; pri ...

  8. JUC 并发编程--09, 阻塞队列: DelayQueue, PriorityBlockingQueue ,SynchronousQueue, 定时任务线程池: ScheduledThreadPoolExecutor

    先看DelayQueue 这个是用优先级队列实现的无界限的延迟队列,直接上代码: /** * 这个是 {@link DelayQueue} 延时队列 的验证使用类 */ class MyDelayed ...

  9. JUC 并发编程--04 常用的辅助类CountDownLatch , CyclicBarrier , Semaphore , 读写锁 , 阻塞队列,CompletableFuture(异步回调)

    CountDownLatch 相当于一个减法计数器, 构造方法指定一个数字,比如6, 一个线程执行一次,这个数字减1, 当变为0 的时候, await()方法,才开始往下执行,, 看这个例子 Cycl ...

随机推荐

  1. python and、or以及and-or

    @Python: and.or以及and-or 一.and: 在Python 中,and 和 or 执行布尔逻辑演算,如你所期待的一样,但是它们并不返回布尔值:而是,返回它们实际进行比较的值之一. & ...

  2. C语言利用 void 类型指针实现面向对象类概念与抽象。

    不使用C++时,很多C语言新手可能认为C语言缺乏了面向对象和抽象性,事实上,C语言通过某种组合方式,可以间接性的实现面对对象和抽象. 不过多态和继承这种实现,就有点小麻烦,但是依然可以实现. 核心: ...

  3. 谷歌希望让 Swift 成为安卓的优先选择,以取代由 Oracle 开发的 Java 程序语言。

    http://news.coolban.com/Web/Index/land/app/2/id/405239

  4. loli的测试——搜索

    今天是2018.5.24,loli给我们说要考搜索,本来以为是给初学者们考的就没准备,然而老师说我们也要考.(2018.6.29补:这次的简单测试与之后变得非常难的几次搜索测试形成了鲜明的对比,从而更 ...

  5. Redis基本数据类型命令汇总

    前言   前阶段写Redis客户端作为学习和了解Redis Protocol,基本上把Strintg,List,Hash,Set,SortedSet五种基础类型的命令都写完了,本篇进行总结,也相当于复 ...

  6. Python中乘法

    1.numpy乘法运算中"*"或multiply(),是数组元素逐个计算,具体代码如下: import numpy as np # 2-D array: 2 x 3 two_dim ...

  7. C语言程序设计I—第九周教学

    第九周教学总结(28/10-03/11) 教学内容 第三章 分支结构 3.3 查询自动售货机中商品的价格 课前准备 在蓝墨云班课发布资源: PTA:2018秋第九周作业1 3.3 分享码:530571 ...

  8. grep 搜索多个同时满足的条件

    (二) Grep AND 操作 1. 使用 -E 'pattern1.*pattern2' grep命令本身不提供AND功能.但是,使用 -E 选项可以实现AND操作.   grep -E 'patt ...

  9. Debian 8 时间同步

    每天执行一次 sudo ntpdate ntp.ubuntu.com 逐渐觉得麻烦了,有没有自动执行的方法? 在Linux中用户可以执行例行性的工作,使用crontab这个命令. 步骤: 1.在终端中 ...

  10. ubuntu 9.10 切换到root用户

    昨天装了ubuntu9.10,登陆后是普通用户,操作不方便,上网上查了资料,有很多方法,我发现最简单的方法 有些资料说,ubuntu每次重启root密码是随机的(当你没有设置密码时), 打开终端: $ ...