生产者-消费者
ArrayBlockingQueue是一个实现了BlockingQueue接口的类,其可以很方便的实现生产者-消费者模式。用法如下:

class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}

class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}

class Setup {
void main() {
BlockingQueue q = new SomeQueueImplementation();
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
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
two-condition算法来进行并发控制
在ArrayBlockingQueue中有如下三个变量声明(定义):

/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/

/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
1
2
3
4
5
6
7
8
9
10
11
12
13
实现生产者-消费者的并发控制很简单,一把锁,两个条件!再来看ArrayBlockingQueue的构造函数代码:

public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
1
2
3
4
5
6
7
8
9
10
在初始化的时候,ArrayBlockingQueue对lock、notEmpty、notFull进行了初始化。

生产者进行生产
首先查看生产者生产时候需要调用的put(E e)方法:

public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
首先通过ReentrantLock的lockInterruptibly()方法来尝试获得锁,该方法在获取锁之后,可以继续响应线程的interrupt操作,注意lock.unlock()一定要写在finally块中,不然在出现异常之后,有可能永远也释放不了锁了!

当发现当前数量已经满的时候:while(count == items.length),那么将会让生产者(当前线程)进行等待:notFull.await(),否则进行insert(e)操作。

继续跟踪insert(e)操作不难想到,在插入成功之后,会通知notEmpty来唤醒消费者(某一个正在等待notEmpty条件的线程),告知有了新的产品可消费了!

private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
}
1
2
3
4
5
6
7
8
如上可知:如果队列已满(full),那么notFull进行等待,否则插入成功之后,唤醒notEmpty告知不用等待了。同理:消费者进行消费的take操作也是类似的。

消费者进行消费

public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}

private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);
items[takeIndex] = null;
takeIndex = inc(takeIndex);
--count;
notFull.signal();
return x;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
概括
整体而言,在有了ReentrantLock、Condition之后,生产者-消费者模式实现起来还是很简单的。ReentrantLock负责加锁释放锁,Condition负责等待唤醒线程。
————————————————
版权声明:本文为CSDN博主「赵坤的个人网站」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/anxiaoyi520/article/details/46670675

ArrayBlockingQueue源码剖析的更多相关文章

  1. 并发编程(八)—— Java 并发队列 BlockingQueue 实现之 ArrayBlockingQueue 源码分析

    开篇先介绍下 BlockingQueue 这个接口的规则,后面再看其实现. 阻塞队列概要 阻塞队列与我们平常接触的普通队列(LinkedList或ArrayList等)的最大不同点,在于阻塞队列的阻塞 ...

  2. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  3. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  4. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  5. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  6. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  7. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

  8. 自己实现多线程的socket,socketserver源码剖析

    1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...

  9. Java多线程9:ThreadLocal源码剖析

    ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...

  10. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

随机推荐

  1. GitHub创建新仓库

    第一步.右上角点击[+],选择[New repository] 第二步.设置一下仓库的基本信息 在如下图的红框位置,输入仓库的名称.描述以及是否公开. 第三步.滑到最下面,点击[Create repo ...

  2. GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU

    本文主要分享在不同环境,例如裸机.Docker 和 Kubernetes 等环境中如何使用 GPU. 跳转阅读原文:GPU 环境搭建指南:如何在裸机.Docker.K8s 等环境中使用 GPU 1. ...

  3. 华为三折屏Mate XT

    相关: https://www.youtube.com/watch?v=vMx3kVnp11Q

  4. 零基础入门Hadoop:IntelliJ IDEA远程连接服务器中Hadoop运行WordCount

    今天我们来聊一聊大数据,作为一个Hadoop的新手,我也并不敢深入探讨复杂的底层原理.因此,这篇文章的重点更多是从实际操作和入门实践的角度出发,带领大家一起了解大数据应用的基本过程.我们将通过一个经典 ...

  5. sqli注入之sqlmap

    善于使用google hacking的搜索语法不失为一种有效的挖洞方法. 利用google浏览器inurl搜索语法寻找可能sql注入的php网站url inrul:productInfo.php?id ...

  6. Ubuntu apt-cache命令查找可用软件包

    本文GoFace给大家讲解下在Ubuntu及相同包管理的linux系统下如何查找可用软件包.在Ubuntu上大家一般使用apt-get安装软件,如果想查找某一包软件仓库中是否有,并不是使用apt-ge ...

  7. php7.4.x~php8.0.x 新特性

    PHP 核心中的新特性 命名参数 新增 命名参数 的功能. // array_fill(int $start_index, int $count, mixed $value): array // 使用 ...

  8. 通过NPOI读取 excel指定Sheet 到 DataTable

    public static DataTable ReadExcelToDataTable(string fileName, string sheetName = null, bool isFirstR ...

  9. cmd操作license服务

    配置客户机的时候,有时候需要自启动相关服务,例如ArcGIS License Manager. 关于启动服务,参考了别人的意见.点击打开链接 最终用了net start 服务名和net sop 服务名 ...

  10. JDK 18 最新动态和 JDK 19 新特性预测

    JDK 18 最新动态和 JDK 19 新特性预测_语言 & 开发_Michael Redlich_InfoQ精选文章 里面提到文章 定界延续(delimited continuations) ...