生产者-消费者
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. 周六晚11实习生上线数据观测突发问题(涉及MYSQL,Hive等)

    前提 有点恐怖,上次需求上线后,部分线上数据观测要留到11月初,上次是一个税收相关的需求,已有功能的线上数据观察已经完成,还剩下部分只有在十一月初才可以观察 简单提一嘴(非技术相关) 之前把hive ...

  2. PHP word PDF excel 文档互转 预览 (linux libreoffice)

    1--卸载系统当前的libreoffice 执行如下命令:yum remove libreoffice-* 2--为了能使用镜像,在etc  目录之下的hosts文件内加入代理. 注意没有加的话,可能 ...

  3. 3.6 Linux命令基本格式

    本节开始,我们不会再见到图形界面了,因为对服务器来讲,图形界面会占用更多的系统资源,而且会安装更多的服务.开放更多的端口,这对服务器的稳定性和安全性都有负面影响.其实,服务器是一个连显示器都没有的家伙 ...

  4. 解决MindSpore-2.4-GPU版本的安装问题

    问题背景 虽说在MindSpore-2.3之后的版本中不在正式的发行版中支持GPU硬件后端,但其实在开发分支版本中对GPU后端是有支持的: 但是在安装的过程中可能会遇到一些问题或者报错,这里复现一下我 ...

  5. 奥迪借助Karpenter为关键业务节省63%成本

    原文链接: https://aws.amazon.com/cn/solutions/case-studies/audi-efficient-compute-case-study/ 翻译:cloudpi ...

  6. 切换自己为www-data用户

    突发奇想的想把切换为www-data用户去看看会怎么样.然后做了一个尝试 由于我安装了lamp环境,所以有www-data用户,用它可以来执行web php ,以及安全放心的跑cli(避免权限过高执行 ...

  7. php 版本升级后需要对代码进行兼容性检测

    来到需要检测代码的目录下 需要提前安装 docker 执行 docker run --rm -v $(pwd):/app vfac/php7compatibility 7.3 . --ignore=v ...

  8. VUE 前端读取excel表格内容

    <el-upload class="upload-demo" :action="''" :show-file-list="false" ...

  9. ArcGIS遥感影像重采样操作及不同算法对比

      本文介绍在ArcMap软件中,实现栅格图像重采样的具体操作,以及不同重采样方法的选择依据.   在之前的博客中,我们介绍了基于Python中Arcpy模块对栅格图像加以批量重采样的方法:而在Arc ...

  10. Java的多线程编程模型5--从AtomicInteger开始(自增长实现)

    AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字.而AtomicIn ...