BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会阻塞。

ArrayBlockingQueue是一个由数组支持的有界阻塞队列。在读写操作上都需要锁住整个容器,因此吞吐量与一般的实现是相似的,适合于实现“生产者消费者”模式。

LinkedBlockingQueue基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

代码:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; public class Main {
public static void main(String[] args) {
final BlockingQueue queue = new ArrayBlockingQueue(3);
//final BlockingQueue queue = new LinkedBlockingQueue(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "准备放数据!");
queue.put(1);
System.out.println(Thread.currentThread().getName() + "已经放了数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
} }
} }.start();
} new Thread(){
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "准备取数据!");
queue.take();
System.out.println(Thread.currentThread().getName() + "已经取走数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }.start();
}
}

1000:

Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有2个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有1个数据
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有2个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有3个数据
Thread-0准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-0已经放了数据,队列目前有3个数据
Thread-1准备放数据!
Thread-0准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-1已经放了数据,队列目前有3个数据
Thread-1准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-0已经放了数据,队列目前有3个数据
Thread-0准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-1已经放了数据,队列目前有3个数据
Thread-1准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-0已经放了数据,队列目前有3个数据
Thread-0准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-1已经放了数据,队列目前有3个数据
Thread-1准备放数据!
Thread-2准备取数据!
Thread-0已经放了数据,队列目前有3个数据
Thread-2已经取走数据,队列目前有3个数据
Thread-0准备放数据!
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有2个数据
Thread-1已经放了数据,队列目前有3个数据
Thread-1准备放数据!
Thread-2准备取数据!

100:

Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-1已经放了数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-1已经放了数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有2个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有1个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有2个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有1个数据
Thread-2准备取数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-0准备放数据!
Thread-2已经取走数据,队列目前有0个数据
Thread-0已经放了数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-2准备取数据!
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-2已经取走数据,队列目前有0个数据
Thread-1准备放数据!
Thread-1已经放了数据,队列目前有1个数据
Thread-0准备放数据!
Thread-0已经放了数据,队列目前有2个数据
Thread-2准备取数据!

ArrayBlockingQueue与LinkedBlockingQueue的区别:

1.    队列中锁的实现不同
ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁;
LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock 2. 在生产或消费时操作不同
ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的;
LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node<E>进行插入或移除,会影响性能 3. 队列大小初始化方式不同
ArrayBlockingQueue实现的队列中必须指定队列的大小;
LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是Integer.MAX_VALUE 注意:
1. 在使用LinkedBlockingQueue时,若用默认大小且当生产速度大于消费速度时候,有可能会内存溢出
2. 在使用ArrayBlockingQueue和LinkedBlockingQueue分别对1000000个简单字符做入队操作时,
LinkedBlockingQueue的消耗是ArrayBlockingQueue消耗的10倍左右,
即LinkedBlockingQueue消耗在1500毫秒左右,而ArrayBlockingQueue只需150毫秒左右。

=======================================================

BlockingQueue具体分析:(Java7源码)

add()和remove()是最原始的方法,也是最不常用的。

原因是,当队列满了或者空了的时候,会抛出IllegalStateException("Queuefull")/NoSuchElementException(),并不符合我们对阻塞队列的要求;

因此,ArrayBlockingQueue里,这两个方法的实现,直接继承自java.util.AbstractQueue:

public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
} public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}

有上述源码可知,add()和remove()实现的关键,是来自java.util.Queue接口的offer()和poll()方法。
offer():在队列尾插入一个元素。若成功便返回true,若队列已满则返回false。
poll():同理,取出并删除队列头的一个元素。若成功便返回元素,若队列为空则返回null。
这里使用的是ReentrantLock,在插入或者取出前,都必须获得队列的锁,以保证同步。

public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
} public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : extract();
} finally {
lock.unlock();
}
}

由于offer()/poll()是非阻塞方法,一旦队列已满或者已空,均会马上返回结果,也不能达到阻塞队列的目的。因此有了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();
}
}

put()/take()的实现,比起offer()/poll()复杂了一些,尤其有两个地方值得注意:

1. 取得锁以后,循环判断队列是否已满或者已空,并加上Condition的await()方法将当前正在调用put()的线程挂起,直至notFull.signal()唤起。

2. 这里使用的是lock.lockInterruptibly()而不是lock.lock()。原因在这里。lockInterruptibly()这个方法,优先考虑响应中断,而不是响应普通获得锁或重入获得锁。简单来说就是,由于put()/take()是阻塞方法,一旦有interruption发生,必须马上做出反应,否则可能会一直阻塞。

最后,无论是offer()/poll()还是put()/take(),都要靠insert()/extract()这个私有方法去完成真正的工作:

private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
} final int inc(int i) {
return (++i == items.length) ? 0 : i;
} 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;
} final int dec(int i) {
return ((i == 0) ? items.length : i) - 1;
}

insert()/extract(),是真正将元素放进数组或者将元素从数组取出并删除的方法。由于ArrayBlockingQueue是有界限的队列(Bounded Queue),因此inc()/dec()方法保证元素不超出队列的界限。

另外,每当insert()后,要使用notEmpty.signal()唤起因队列空而等待取出的线程;每当extract()后,同理要使用notFull.signal()唤起因队列满而等待插入的线程。

列举一些使用队列时的错误做法:

1. 不能忽略offer()的返回值。offer()作为有返回值的方法,可以在判断的时候十分有作用(例如add()的实现)。因此,千万不要忽略offer()方法的返回值。

2. 在循环里使用isEmpty()和阻塞方法:

take()是阻塞方法,无需做isEmpty()的判断,直接使用即可。而这种情况很有可能导致死锁,因为由于不断循环,锁会一直被isEmpty()取得(因为size()方法会取得锁),而生产者无法获得锁。

3. 频繁使用size()方法去记录。size()方法是要取得锁的,意味着这不是一个廉价的方法。可以使用原子变量代替。

阻塞队列的操作:

add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
offer       添加一个元素并返回true       如果队列已满,则返回false
poll         移除并返问队列头部的元素    如果队列为空,则返回null
peek       返回队列头部的元素             如果队列为空,则返回null
put         添加一个元素                      如果队列满,则阻塞
take        移除并返回队列头部的元素     如果队列为空,则阻塞

remove、element、offer 、poll、peek 其实是属于Queue接口。

http://www.cnblogs.com/liuling/p/2013-8-20-01.html

http://www.cnblogs.com/techyc/p/3782079.html?utm_source=tuicool&utm_medium=referral

http://blog.csdn.net/luohuacanyue/article/details/16359777

http://jiangzhengjun.iteye.com/blog/683593

ArrayBlockingQueue,LinkedBlockingQueue分析的更多相关文章

  1. ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue, RingBuffer

    1. ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue ArrayBlockingQueue, LinkedBlocking ...

  2. ArrayBlockingQueue和LinkedBlockingQueue分析

        JAVA并发包提供三个常用的并发队列实现,分别是:ConcurrentLinkedQueue.LinkedBlockingQueue和ArrayBlockingQueue. Concurren ...

  3. 高并发第十三弹:J.U.C 队列 SynchronousQueue.ArrayBlockingQueue.LinkedBlockingQueue.LinkedTransferQueue

    因为下一节会说线程池,要用线程池 那么线程池有个很重要的参数 就是Queue的选择 常用的队列其实就两种: 先进先出(FIFO):先插入的队列的元素也最先出队列,类似于排队的功能.从某种程度上来说这种 ...

  4. 3.3.6-1 ArrayBlockingQueue简单分析

    构造方法:public ArrayBlockingQueue(int capacity) { this(capacity, false); } public ArrayBlockingQueue(in ...

  5. LinkedBlockingQueue与ArrayBlockingQueue

    阻塞队列与普通的队列(LinkedList/ArrayList)相比,支持在向队列中添加元素时,队列的长度已满阻塞当前添加线程,直到队列未满或者等待超时:从队列中获取元素时,队列中元素为空 ,会将获取 ...

  6. java并发包——阻塞队列BlockingQueue及源码分析

    一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...

  7. 细说并发5:Java 阻塞队列源码分析(下)

    上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...

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

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

  9. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

随机推荐

  1. 剑指Offer——字符流中第一个不重复的字符

    题目描述: 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读 ...

  2. spring常见注解说明

    1. @ActiveProfiles("test") 我理解这个注解的主要用途是区分不同的环境.一般公司开发一个项目时,会区分测试环境.生产环境等.添加该注解,说明读取的profi ...

  3. python爬虫系列(1)——一个简单的爬虫实例

    本文主要实现一个简单的爬虫,目的是从一个百度贴吧页面下载图片. 1. 概述 本文主要实现一个简单的爬虫,目的是从一个百度贴吧页面下载图片.下载图片的步骤如下: 获取网页html文本内容:分析html中 ...

  4. 《从零開始搭建游戏server》项目公布到Linux环境

    前言: 之前我们提及了怎样使用Maven来创建.管理和打包项目,也简单过了一遍Linux中搭建Java开发环境的步骤,如今我们就開始将我们之前开发的项目demo公布到Linux环境下.并让它正常执行起 ...

  5. python web框架 django工程的创建

    安装 django pip3 install django pip install django 安装完后出现这两个文件 django-admin 用来创建文件夹 在script目录 执行这个命令 d ...

  6. SDUT3145:Integer division 1(换零钱背包)

    题目:传送门 题目描述 整数划分是一个非常经典的数学问题. 所谓整数划分,是指把一个正整数n写成为n=m1+m2+...+mi的形式,其中mi为正整数,并且1<=mi<=n,此时,{m1, ...

  7. Linux命令 lsof使用

    lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件. lsof +d ...

  8. AngularJS多模块开发与路由

    这里只是做一个笔记 angularjs模块(父子级)比如我有一个项目叫做shcool,那么我school下边有两个模块,student.teacher.此时school就属于主模块,其他都是子模块.子 ...

  9. ionic调用摄像头

    须知ionic是借助于cordova来实现与底层硬件的交互的 我假设你已经全局安装了cnpm.ionic.cordova,并且配置好了安卓环境 我以下demo是建立在官方提供的模板上进行的ionic ...

  10. 47. Permutations II (全排列有重复的元素)

    Given a collection of numbers that might contain duplicates, return all possible unique permutations ...