概述

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. log4j 配置详解

    参考如下两个网址,讲的很详细,先看第一个再看第二个: log4j使用介绍:http://swiftlet.net/archives/683 java日志处理组件log4j--log4j.xml配置详解 ...

  2. HTML基础内容(持续更新...)

    1.<!DOCTYPE html>声明有助于浏览器中正确显示网页 HTML5<!DOCTYPE html>HTML 4.01<!DOCTYPE HTML PUBLIC & ...

  3. JDBC操作数据库的基本步骤:

    JDBC操作数据库的基本步骤: 1)加载(注册)数据库驱动(到JVM). 2)建立(获取)数据库连接. 3)创建(获取)数据库操作对象. 4)定义操作的SQL语句. 5)执行数据库操作. 6)获取并操 ...

  4. 微软技术大会直播倒计时7天 | 地球上的IT咖们,正在慢慢向北京聚集

    去年,一句歌词火了:“我在北方的寒夜里,四季如春.”足以见得,北京的冬天,是可以把人冻蒙圈儿的. 然而有一群人,却正在慢慢靠近这样寒冷的北京,7 天后,他们将齐聚在北京. 他们来这里干什么?又是来见谁 ...

  5. python 学习 (迟到的2017小结)

    2017过去了,从前端开发转测试,入行一年多,写了web的UI自动化测试框架,看了python基础教程,核心编程,算是入门python了吧.目前呢,想开发个接口的自动化测试平台.           ...

  6. 三、python webservice

    #!/usr/bin/python # -*- coding: utf-8 -*- import logging import suds url="http://172.17.2.199:8 ...

  7. unity的默认文件目录及脚本之间的执行顺序

    在unity中,有几个默认的文件目录,这些目录对于开发者来说是非常重要的,今天记录一下这些目录的功能和一些相关的知识 大神雨松给出的帖子我个人觉得还是很有价值的,附上链接 http://www.xua ...

  8. Ubuntu环境安装Gradle

    AndroidStudio使用全新的构建系列—–Gradle. 这是官方为什么使用gradle 的理由: Domain Specific Language (DSL) to describe and ...

  9. Android获取手机安装的浏览器列表

    最近碰到一个同事询问如何查询本地安装的浏览器列表,其使用的代码如下: public static List<ResolveInfo> getBrowserList(Context cont ...

  10. SPOJ MUSKET - Musketeers

    黑书P117页. 要是不看解析,确实不要算和定义状态. 把环看成链,是指把这个1234512345,写两边,然后怎么表示一个人是否胜利了呢?其实就是其他人全部死光(好像等于没说): 考虑最后一次杀人, ...