• BlockingQueue简介

  ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

  LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE,每次插入后都将动态地创建链接节点。

  PriorityBlockingQueue:以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素,依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。

  DelayQueue:基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

  SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。

  • BlockingQueue内容

  BlockingQueue主要方法:

  抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
检查 element() peek() 不可用 不可用

  对于非阻塞队列,一般情况下建议使用offer、poll和peek三个方法,不建议使用add和remove方法。因为使用offer、poll和peek三个方法可以通过返回值判断操作成功与否,而使用add和remove方法却不能达到这样的效果。注意,非阻塞队列中的方法都没有进行同步措施。

  • BlockingQueue实现原理

  以ArrayBlockingQueue为例,查看其源代码,其中主要包含以下对象:

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -817911632652898426L; /** 数组对象,用于放置对象 */
final Object[] items; /** put, offer, or add方法放入数组的索引 */
int putIndex; /** take, poll, peek or remove方法取出数据的数组索引 */
int takeIndex;
/** queue队列的总数 */
int count; /**可重入锁,控制并发*/
final ReentrantLock lock;
/** 非空信号量,可以取数*/
private final Condition notEmpty;
/** 非满信号量,可以放数 */
private final Condition notFull;
}

  下面主要介绍下put()和take()方法,来观察其同步的实现:

 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();
}
}
 public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}

  大家应该明白了阻塞队列的实现原理,事实它和我们用Object.wait()、Object.notify()和非阻塞队列实现生产者-消费者的思路类似,只不过它把这些工作一起集成到了阻塞队列中实现。并且在前面Condition中我们也模拟实现了一个阻塞队列,实现与其大同小异。

  • BlockingQueue应用

  1:启动两个线程实现互斥等待:

 public class BlockingQueueTest {
public static void main(String[] args) {
final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Thread "+Thread.currentThread().getName()+"正在准备放入数据");
try {
//模拟线程的放数速度
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
queue.put(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Thread "+Thread.currentThread().getName()+"放入数据,此时队列中的数据为:"+queue.size());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Thread "+Thread.currentThread().getName()+"正在取得数据");
try {
//模拟线程的去数速度
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
queue.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Thread "+Thread.currentThread().getName()+"取得数据,此时队列中的数据为:"+queue.size());
}
}
}).start();
} }
}

  2:前面介绍传统线程通信中,主线程和子线程交替运行,现在以阻塞队列来实现。

 public class BlockingQueueCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
try {
business.sub(i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
for (int i = 0; i < 50; i++) {
try {
business.main(i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class Business{
BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1);
BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1);
{
try {
queue2.put(1);//保证queue2阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} public void main(int i) throws InterruptedException{
queue1.put(1);//阻塞queue1
for (int j = 0; j < 100; j++) {
System.out.println("main thread is looping of "+j +" in " + i);
}
queue2.take();//唤醒queue2
}
public void sub(int i) throws InterruptedException{
queue2.put(1);//阻塞queue2
for (int j = 0; j < 10; j++) {
System.out.println("sub thread is looping of "+j +" in " + i);
}
queue1.take();//唤醒queue1
}
}
}
  BlockingQueue实现了线程同步,不可在方法中再次加入同步限制,否则会出现死锁。

  3:在API中有一个阻塞对象实现生产者和消费者的例子

 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();
}
}

  使用阻塞队列代码要简单得多,不需要再单独考虑同步和线程间通信的问题。

  在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。

  阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。

  参考资料:http://www.cnblogs.com/dolphin0520/p/3932906.html

       javaAPI

java多线程-BlockingQueue的更多相关文章

  1. Java多线程系列十——BlockingQueue

    参考资料:http://ifeve.com/java-synchronousqueue/http://www.cnblogs.com/jackyuj/archive/2010/11/24/188655 ...

  2. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  3. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  4. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  5. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  6. java多线程编程

    一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复 ...

  7. Java多线程总结之线程安全队列Queue

    在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非 ...

  8. Java多线程20:多线程下的其他组件之CountDownLatch、Semaphore、Exchanger

    前言 在多线程环境下,JDK给开发者提供了许多的组件供用户使用(主要在java.util.concurrent下),使得用户不需要再去关心在具体场景下要如何写出同时兼顾线程安全性与高效率的代码.之前讲 ...

  9. Java多线程系列--“JUC集合”07之 ArrayBlockingQueue

    概要 本章对Java.util.concurrent包中的ArrayBlockingQueue类进行详细的介绍.内容包括:ArrayBlockingQueue介绍ArrayBlockingQueue原 ...

随机推荐

  1. PHP通过串口发短信

    随技术进步,短信收发领域按时间先后产生了三种模式:BLOCK MODE,基于AT指令的TEXT MODE,基于AT指令的PDU MODE.其中,TEXT MODE比较简单,多款诺基亚手机均支持此款模式 ...

  2. spring hadoop 访问hbase入门

    1.  环境准备: Maven Eclipse Java Spring 版本 3..2.9 2. Maven  pom.xml配置 <!-- Spring hadoop --> <d ...

  3. selenium获取html的表格单元格数据

    获取网页的表格的某个单元格的值,思路: 1.获取表格 2.获取表格的所有行 3.根据某一行获取该行的所有列 4.根据某一列获得该行该列的单元格值 根据以上思路,可以知道,只需要行.列就可以得到单元格的 ...

  4. Axure学习笔记1--原型设计概述

    Axure原型 1.原型的出现 -软件功能复杂,用户需求多 -挖掘用户的实际需求 -项目组之间降低沟通成本 2.类型: [草图原型]描述产品大概需求,记录瞬间灵感 [低保真原型]展示系统的大致结构和基 ...

  5. [ZZ]Sign Up for the First-Ever Appium Roadshow on August 20th in New York City

    http://sauceio.com/index.php/2014/07/appium-roadshow-nyc/?utm_source=feedly&utm_reader=feedly&am ...

  6. CLOSE_WAIT?项目上线之际遇到这样的烦心事

    项目内测中,马上就要发布了,如今内测,所以很忙,今天运维那发来一堆状态,忘记截图了,简单来讲就是HTTP发送请求的时候有连接等待关闭,导致CLOSE_WAIT这个状态一直累加,没有释放,这样长时间下去 ...

  7. android studio 中的编码问题

    在 Android studio 中直接创建项目和导入其他项目都会有一个文件编码设定的问题,在 android studio (version 1.2.0)中设置文件的编码,只需要两步: 1.打开Se ...

  8. ios开发中如何隐藏各种bar

    转载自http://www.cnblogs.com/lovecode/articles/2234557.html 状态条Status Bar [UIApplication sharedApplicat ...

  9. 13Spring_AOP编程(AspectJ)_后置通知

    后置通知和前置通知差不多.最大的特点是因为后置通知是运行在目标方法之后的,所以他可以拿到目标方法的运行的结果. 给出案例: 案例结构图:

  10. Install MySQL on Mac OS X——MAC安装MySQL

    很多关于如何安装MySQL的教程已经过时了,或者比必须的步骤复杂得多.这篇教程将展示如何安装MySQL,启动MySQL,以root用户进入MySQL,以及创建删除退出数据库. Step 1: 下载My ...