这篇文章将使用经典的生产者消费者的例子来进一步巩固java多线程通信,介绍使用阻塞队列来简化程序

下面是一个经典的生产者消费者的例子:

假设使用缓冲区存储整数,缓冲区的大小是受限制的。缓冲区提供write(int)方法将一个整数添加到缓冲区,还体统read()方法从缓冲区中读取并删除一个整数。为了同步操作,使用具有两个条件的锁,notEmpty(缓冲区非空)和notFull(缓冲区未满)。当任务相缓冲区添加一个int时,如果缓冲区是满的,那么任务将等待notFull状态,当任务从缓冲区总删除一个int时,如果缓冲区是空的,那么任务将等待notEmpty状态。

import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ConsumerProducer { private static Buffer buffer = new Buffer(); public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new Producertask());
executor.execute(new Consumertask());
executor.shutdown();
} private static class Producertask implements Runnable {
@Override
public void run() { try {
int i = 1;
while (true) {
System.out.println("Producer writes " + i);
buffer.write(i++);
Thread.sleep((int) (Math.random() * 10000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private static class Consumertask implements Runnable {
public void run() {
try {
while (true) {
System.out.println("\t\t\tConsumer reads " + buffer.read());
Thread.sleep((int) (Math.random() * 10000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private static class Buffer {
private static final int CAPACITY = 1; // buffer size
LinkedList<Integer> queue = new LinkedList<Integer>(); // 创建锁
private static Lock lock = new ReentrantLock(); // 创建两个条件
private static Condition notEmpty = lock.newCondition();
private static Condition notFull = lock.newCondition(); public void write(int value) {
lock.lock(); // 请求锁
try {
while (queue.size() == CAPACITY) {
System.out.println("Wait for notFull condition");
notFull.await();
}
queue.offer(value);
notEmpty.signal(); // notEmpty条件信号 } catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} @SuppressWarnings("finally")
public int read() {
int value = 0;
lock.lock();
try {
while (queue.isEmpty()) {
System.out.println("\t\t\tWait for notEmpty condition");
notEmpty.await();
}
value = queue.remove();
notFull.signal(); } catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
return value;
}
}
}
}

阻塞列队

阻塞列队在试图想一个满列队添加元素或这从空列队删除元素时会导致线程阻塞。BlockQueue接口扩展java.util.queue,并且提供同步的put和take方法想列队头部添加元素,以及从列队尾删除元素

java支持三个具体的阻塞列队ArrayBlockingQueue、LinkedblockingQueue 和 PriorityBlockingQueue ,他们都在java.util.concurrent包中。

ArrayBlockingQueue

一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。

这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

LinkedBlockingQueue

一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。

可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。如果未指定容量,则它等于Integer.MAX_VALUE。除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点。

PriorityBlockingQueue 

一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。

使用ArrayBlockingQueue简化后的代码如下:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ConsumerProducerUsingBlockingQueue {
private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<Integer>(2); public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(new Producertask());
executor.execute(new Consumertask());
executor.shutdown();
}
private static class Producertask implements Runnable {
@Override
public void run() { try {
int i = 1;
while (true) {
System.out.println("Producer writes " + i);
buffer.put(i++);
Thread.sleep((int) (Math.random() * 10000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private static class Consumertask implements Runnable {
public void run() {
try {
while (true) {
System.out.println("\t\t\tConsumer reads " + buffer.take());
Thread.sleep((int) (Math.random() * 10000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }

可以看到,代码减少了一半,主要是因为ArrayBlockingQueue中已经实现了同步,所以无需手动编码

java多线程系列6-阻塞队列的更多相关文章

  1. java多线程系列10 阻塞队列模拟

    接下来的几篇博客会介绍下juc包下的相关数据结构 包含queue,list,map等 这篇文章主要模拟下阻塞队列. 下面是代码 import java.util.LinkedList; import ...

  2. Java多线程-新特征-阻塞队列ArrayBlockingQueue

    阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素 ...

  3. java多线程8:阻塞队列与Fork/Join框架

    队列(Queue),是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的. BlockingQueue 而阻塞队列BlockingQueue除了继承 ...

  4. Java多线程系列——线程阻塞工具类LockSupport

    简述 LockSupport 是一个非常方便实用的线程阻塞工具,它可以在线程内任意位置让线程阻塞. 和 Thread.suspend()相比,它弥补了由于 resume()在前发生,导致线程无法继续执 ...

  5. Java多线程系列- DelayQueue延时队列

    我们在开发中,有如下场景 a) 关闭空闲连接.服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之.b) 缓存.缓存中的对象,超过了空闲时间,需要从缓存中移出.c) 任务超时处理.在网络协议滑动窗 ...

  6. java多线程系列 目录

    Java多线程系列1 线程创建以及状态切换    Java多线程系列2 线程常见方法介绍    Java多线程系列3 synchronized 关键词    Java多线程系列4 线程交互(wait和 ...

  7. java多线程系列8-线程的优先级

    在java中设置线程优先级使用setPriority,在jdk中的源代码如下: public final void setPriority(int newPriority) { ThreadGroup ...

  8. Java多线程系列——从菜鸟到入门

    持续更新系列. 参考自Java多线程系列目录(共43篇).<Java并发编程实战>.<实战Java高并发程序设计>.<Java并发编程的艺术>. 基础 Java多线 ...

  9. Java并发包源码学习系列:阻塞队列实现之ArrayBlockingQueue源码解析

    目录 ArrayBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e) ...

随机推荐

  1. 【NS2仿真】RTP协议安装

    来自: http://personales.upv.es/fboronat/Research/NS2_RTP/NS2_RTP_RTCP_module.htm 文件:http://pan.baidu.c ...

  2. 从window.console&&console.log(123)浅谈JS的且运算逻辑(&&)

    一.JS的且运算记得最开始看到window.console&&console.log(123),当时知道能起什么作用但是没有深入研究,最近在研究后总算弄明白了.要理解这个,首先得明白三 ...

  3. CSS3 垂直居中 左右居中

    display: -webkit-box; -webkit-box-orient: horizontal; -webkit-box-pack: center; -webkit-box-align: c ...

  4. jquery 按钮效果 正常、移上、按下

    在网页设计过程中,经常遇见按钮的各状态效果.写了一个jquery扩展,使这个过程更方便! 使用前注意引用Jquery: JqueryExtend.js: (function ($) { // Butt ...

  5. ubuntu14.04 编译安装gcc-5.3.0

    最近编译个源码,要求对C++14的支持了,就GCC的编译安装最新的5.3.0,整个过程以root用户进行. 1.下载GCC源码,属于事后文档整理,已经不知道从哪下载了. 2.解压:tar -zxvf ...

  6. teamcity设置

    建立项目后首先要设置svn地址,并绑定 名字随便起 url是svn的地址 之后来做build step 有一个按钮可以自动检测,一般都能检测出来 执行这个似乎需要代理装什么东西 可以查看目前代理有那些 ...

  7. [转载]部署Office Web Apps Server并配置其与SharePoint 2013的集成

    Office Web Apps Server 是新的 Office 服务器产品,它提供 Word.PowerPoint.Excel 和 OneNote 的基于浏览器的版本.单个 Office Web ...

  8. android和ios,音频互通方案

    好久不更新博客上,从年前从公司辞职,这半年以来,一直靠做一些外包app养活自己!也算是达成了自己年前制定的目标!可是也想着总不能一直做外包吧,所以决定做一些自己觉得有意思的app,挂到应用商店上和ap ...

  9. 合并多个dll为一个dll

    有时候自己写个小工具或者其它啥的物件,引用了好多第三方控件,如log4net,aspnetpager啥的,发布出去让别人看到自己竟然用了这么多的第三方DLL, 会对自己的能力产生怀疑,那有什么办法可以 ...

  10. Jquery 前端模版

    <div id="div_demo"></div><script id="demo" type="text/x-jque ...