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. Windows10自动更新之后,无线有线都连不上网

    大概浪费了我至少6个小时. 一个是无线网卡,这个后来可以修复,其实也不是网卡的原因.最主要的原因是 Realtek PCIe GBE Family Controller  这个驱动.只找到一个win1 ...

  2. Service Mesh服务网格之Linkerd架构

    今天详细介绍一下Linkerd的架构. 控制平面 Linkerd控制平面是一组在专用Kubernetes命名空间中运行的服务(在Linked默认情况下).这些服务完成各种事情——聚合遥测数据.提供面向 ...

  3. [错误记录]python requests库 Response 判断坑

    在requests访问之后, 我直接判断resp的值, 如下: if resp: do something 发现当Response 为500的时候没有进入if分支, 检查源码,发现Response重写 ...

  4. msf提权基础(一)

    令牌(token)相当于系统的临时密钥(账号及密码) 加载incognito模块 meterpreter> use incognito meterpreter > list_tokens ...

  5. 404 Note Found队 福大助手项目测评

    目录 第一部分 调研,评测 福大助手的bug IOS端 Android端 福大助手结构体系的思维导图 为什么开发人员没有发现这个bug 假设团队开发这款app,应注意哪些方面(架构.部署运维.微服务等 ...

  6. struts2框架 转载 精华帖

    一.Struts2简介 参考<JavaEE 轻量级框架应用与开发—S2SH> Struts框架是流行广泛的一个MVC开源实现,而Struts2是Struts框架的新一代产品,是将Strut ...

  7. MySql修改WordPress密码

    首先,需要几样东西:Xshell,MySql的密码 具体过程:这里我的服务器用的是Vultr, 登上数据库: 使用的语句是:mysql -u root -p 出现的结果如下图: 接着输入语句:show ...

  8. NAS、SAN、ISCSI存储

    前提补充:linux下只要能够mount存储盘到目录,则这个存储盘必定是格式化过了的 NAS1.客户端通过TCP/IP协议连接存储服务器的存储卷.2.客户端---网络交换机---存储服务器3.存储服务 ...

  9. docker的简单使用----适用于新手

    Docker 1.简介 Docker是一个开源的应用容器引擎 将软件编译成一个镜像:然后在镜像里各种软件做好配置,将镜像发布出去,其他的使用这就可以直接使用这个镜像.运行中的这个镜像叫做容器,容器启动 ...

  10. 【10.21总结】一个渗透测试练习实例——发现未知的漏洞(Race condition)

    Write-up地址:Exploiting an unknown vulnerability 作者:Abhishek Bundela 这篇文章跟我之前看到的文章不太一样,作者是按照一个练习的方式简单描 ...