这篇文章将使用经典的生产者消费者的例子来进一步巩固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. MyBatis知多少(10)应用程序数据库

    应用程序数据库往往是最小.最简单.也最易于使用的数据库.这种数据库往往是我们这些开发人员通常不介意使用甚至非常乐意使用的.应用程序数据库通常与我们的应用程序处于同一个项目中,两者一齐设计和实现.正是因 ...

  2. 玩转PowerShell第二节——【利用PsExec进行远程调用】-技术&分享

    概述 PowerShell用的最多的地方就是远程调用,在远程机器上执行脚本,监控远程机器的状态,如NLB状态,EventLog,SqlServer DataBase状态等. 本篇将讲到用PsExec. ...

  3. IE下angularJS页面跳转的bug

    用Angularjs做项目的过程中遇到一种情况:就是在IE浏览器下,当访问网站页面后,点击浏览器中的向左和向右(返回和前进)按钮时,需要点击两次才能正确跳转,但是在chrome及其他浏览器下该bug没 ...

  4. Java定位CPU使用高问题--转载

    原文地址:http://www.cnblogs.com/guoyuqiangf8/p/3545687.html 1.TOP命令,查询消耗CPU高的进程号 PID,并记录下来,按下键盘"H&q ...

  5. python面试题大全(二)

    转载请注明出处http://www.cnblogs.com/goodhacker/p/3387027.html 1.python中类方法.类实例方法.静态方法有何区别? 区别: 类方法和静态方法都可以 ...

  6. JAVA jdbc(数据库连接池)学习笔记(一)

    学习内容: 1.JDBC的含义... JDBC想必学过JAVA的就不会陌生,JDBC到底是什么呢?其实就是由JAVA的一些类和接口构成的API,保存在java.sql和javax.sql..包中的一些 ...

  7. 为什么NOLOCK查询提示是个不明智的想法

    一些人总当NOLOCK查询提示是SQL Server里的加速器,因为它避免了大量的死锁情景.在这篇文章里,我想向你展示下为什么NOLOCK查询提示是个不好的想法. 脏读(Dirty Reads) NO ...

  8. json,serialize,msgpack比较

    速度 在redis中存入同样的压缩数据,取操作执行两个操作: 1 从redis中取 2 解压 3 统一json压缩后放出   ab测试: [yejianfeng@openstack ~/httpd/u ...

  9. 我理解的Android加载器

    Android的加载器(loader)是从Android 3.0开始出来的东西.要理解这里需要先理解为什么会出现加载器(也有地方把它说成是装载器)呢? 如果没有加载器... 首先Activity是我们 ...

  10. Java向前引用容易出错的地方

    所谓向前引用,就是在定义类.接口.方法.变量之前使用它们,例如, class MyClass { void method() { System.out.println(myvar); } String ...