概述

LinkedBlockingQueue内部由单链表实现,只能从head取元素,从tail添加元素。添加元素和获取元素都有独立的锁,也就是说LinkedBlockingQueue是读写分离的,读写操作可以并行执行。LinkedBlockingQueue采用可重入锁(ReentrantLock)来保证在并发情况下的线程安全。

构造器

LinkedBlockingQueue一共有三个构造器,分别是无参构造器、可以指定容量的构造器、可以穿入一个容器的构造器。如果在创建实例的时候调用的是无参构造器,LinkedBlockingQueue的默认容量是Integer.MAX_VALUE,这样做很可能会导致队列还没有满,但是内存却已经满了的情况(内存溢出)。

 public LinkedBlockingQueue();   //设置容量为Integer.MAX

 public LinkedBlockingQueue(int capacity);  //设置指定容量

 public LinkedBlockingQueue(Collection<? extends E> c);  //穿入一个容器,如果调用该构造器,容量默认也是Integer.MAX_VALUE

LinkedBlockingQueue常用操作

取数据

take():首选。当队列为空时阻塞

poll():弹出队顶元素,队列为空时,返回空

peek():和poll烈性,返回队队顶元素,但顶元素不弹出。队列为空时返回null

remove(Object o):移除某个元素,队列为空时抛出异常。成功移除返回true

添加数据

put():首选。队满是阻塞

offer():队满时返回false

判断队列是否为空

size()方法会遍历整个队列,时间复杂度为O(n),所以最好选用isEmtpy

put元素原理

基本过程:

1.判断元素是否为null,为null抛出异常

2.加锁(可中断锁)

3.判断队列长度是否到达容量,如果到达一直等待

4.如果没有队满,enqueue()在队尾加入元素

5.队列长度加1,此时如果队列还没有满,调用signal唤醒其他堵塞队列

  if (e == null) throw new NullPointerException();

         int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}

take元素原理

基本过程:

1.加锁(依旧是ReentrantLock),注意这里的锁和写入是不同的两把锁

2.判断队列是否为空,如果为空就一直等待

3.通过dequeue方法取得数据

3.取走元素后队列是否为空,如果不为空唤醒其他等待中的队列

 public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}

enqueue()和dequeue()方法实现都比较简单,无非就是将元素添加到队尾,从队顶取走元素,感兴趣的朋友可以自己去看一下,这里就不粘贴了。

LinkedBlockingQueue与LinkedBlockingDeque比较

LinkedBlockingDeque和LinkedBlockingQueue的相同点在于: 
1. 基于链表 
2. 容量可选,不设置的话,就是Int的最大值

和LinkedBlockingQueue的不同点在于: 
1. 双端链表和单链表 
2. 不存在哨兵节点 
3. 一把锁+两个条件

实例:

小记:AtomicInteger的getAndIncrment和getAndDcrement()等方法,这些方法分为两步,get和increment(decrement),在get和increment中间可能有其他线程进入,导致多个线程get到的数值是相同的,也会导致多个线程累加后的值其实累加1.在这种情况下,使用volatile也是没有效果的,因为get之后没有对值进行修改,不能触发volatile的效果。

 public class ProducerAndConsumer {
public static void main(String[] args){ try{
BlockingQueue queue = new LinkedBlockingQueue(5); ExecutorService executor = Executors.newFixedThreadPool(5);
Produer producer = new Produer(queue);
for(int i=0;i<3;i++){
executor.execute(producer);
}
executor.execute(new Consumer(queue)); executor.shutdown();
}catch (Exception e){
e.printStackTrace();
} }
} class Produer implements Runnable{ private BlockingQueue queue;
private int nums = 20; //循环次数 //标记数据编号
private static volatile AtomicInteger count = new AtomicInteger();
private boolean isRunning = true;
public Produer(){} public Produer(BlockingQueue queue){
this.queue = queue;
} public void run() {
String data = null;
try{
System.out.println("开始生产数据");
System.out.println("-----------------------"); while(nums>0){
nums--;
count.decrementAndGet(); Thread.sleep(500);
System.out.println(Thread.currentThread().getId()+ " :生产者生产了一个数据");
queue.put(count.getAndIncrement());
}
}catch(Exception e){
e.printStackTrace();
Thread.currentThread().interrupt();
}finally{
System.out.println("生产者线程退出!");
}
}
} class Consumer implements Runnable{ private BlockingQueue queue;
private int nums = 20;
private boolean isRunning = true; public Consumer(){} public Consumer(BlockingQueue queue){
this.queue = queue;
} public void run() { System.out.println("消费者开始消费");
System.out.println("-------------------------"); while(nums>0){
nums--;
try{
while(isRunning){
int data = (Integer)queue.take();
Thread.sleep(500);
System.out.println("消费者消费的数据是" + data);
} }catch(Exception e){
e.printStackTrace();
Thread.currentThread().interrupt();
}finally {
System.out.println("消费者线程退出!");
} }
}
}

效果:

 12 :生产者生产了一个数据
11 :生产者生产了一个数据
13 :生产者生产了一个数据
12 :生产者生产了一个数据
消费者消费的数据是-3
11 :生产者生产了一个数据
13 :生产者生产了一个数据
12 :生产者生产了一个数据
消费者消费的数据是-3
13 :生产者生产了一个数据
11 :生产者生产了一个数据
12 :生产者生产了一个数据
消费者消费的数据是-3
13 :生产者生产了一个数据
11 :生产者生产了一个数据
消费者消费的数据是-3
消费者消费的数据是-3

可以看到,有多个producer在生产数据的时候get到的是相同的值。

阻塞队列之LinkedBlockingQueue的更多相关文章

  1. 11.并发包阻塞队列之LinkedBlockingQueue

    在上文<10.并发包阻塞队列之ArrayBlockingQueue>中简要解析了ArrayBlockingQueue部分源码,在本文中同样要介绍的是Java并发包中的阻塞队列LinkedB ...

  2. java阻塞队列之LinkedBlockingQueue

    LinkedBlockingQueue是BlockingQueue中的其中一个,其实现方式为单向链表,下面看其具体实现.(均为JDK8) 一.构造函数 在LinkedBlockingQueue中有三个 ...

  3. 用Java如何设计一个阻塞队列,然后说说ArrayBlockingQueue和LinkedBlockingQueue

    前言 用Java如何设计一个阻塞队列,这个问题是在面滴滴的时候被问到的.当时确实没回答好,只是说了用个List,然后消费者再用个死循环一直去监控list的是否有值,有值的话就处理List里面的内容.回 ...

  4. Java中的阻塞队列

    1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用 ...

  5. java并发编程学习: 阻塞队列 使用 及 实现原理

    队列(Queue)与栈(Stack)是数据结构中的二种常用结构,队列的特点是先进先出(First In First Out),而Stack是先进后出(First In Last Out),说得通俗点: ...

  6. java并发:阻塞队列

    第一节 阻塞队列 1.1 初识阻塞队列 队列以一种先进先出的方式管理数据,阻塞队列(BlockingQueue)是一个支持两个附加操作的队列,这两个附加的操作是:在队列为空时,获取元素的线程会等待队列 ...

  7. 聊聊并发(七)——Java中的阻塞队列

    3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...

  8. Java并发编程(十二)-- 阻塞队列

    在介绍Java的阻塞队列之前,我们简单介绍一下队列. 队列 队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向 ...

  9. 阻塞队列 BlockingQueue 详解

    转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487078&idx=2&sn=315f39b6d53 ...

随机推荐

  1. Python学习系列提升篇------字符串

    字符串是python学习中重要的内容,在以后的工作中,对字符串的处理也必少不了.下面总结一下关于字符串学习的经验. 1.1 字符串的连接和合并 用‘ + ’连接,将两个字符串相加.       合并, ...

  2. greenplum维护

    1.用户管理 psql -d sea CREATE DATABASE BI; CREATE USER ubi WITH PASSWORD 'pwdbi' NOSUPERUSER; GRANT ALL ...

  3. 別人寫的git的總結,寫自己這裡學習用

    這裡是原文,http://www.cnblogs.com/ang-/p/7352909.html 貼這裡慢慢學. git入门大全   阅读目录 前言 基本概念 文件几种状态 创建新仓库 配置 检出仓库 ...

  4. 转载: Centos7 升级python3,解决升级后不兼容问题

    Centos7配置更新国内yum源 http://blog.csdn.net/qingguiyu/article/details/50721956 Centos7 升级python3,解决升级后不兼容 ...

  5. Master HA彻底解密

    本课主题 Master HA 解析 Master HA 解析源码分享 Master HA 解析 生产环境下一般采用 ZooKeeper 做 HA,且建义为 3台 Master, ZooKeeper 会 ...

  6. mysqlimport导入简单测试

    1    创建一个文本文档:[mysql@xxxycrdb]$ more /tmp/ldcmd1.txt 1,abc,abc@qq.com1,abc,abc@qq.com1,abc,abc@qq.co ...

  7. SQL server 2008 安装报错 reporting services catalog database file existence

    SQL server 2008 安装时报错 提示 reporting services catalog database file existence 查了一下,是因为原来装过Sql server 2 ...

  8. Kubernetes API server工作原理

    作为Kubernetes的使用者,每天用得最多的命令就是kubectl XXX了. kubectl其实就是一个控制台,主要提供的功能: 1. 提供Kubernetes集群管理的REST API接口,包 ...

  9. windows网络模型之重叠IO(完成例程)的使用

    #include <WINSOCK2.H> #include <stdio.h> #define PORT 5150 #define MSGSIZE 1024 #pragma ...

  10. POSIX多线程—线程基本概念

    http://blog.csdn.net/livelylittlefish/article/details/7957007 作者:阿波链接:http://blog.csdn.net/livelylit ...