阻塞队列之LinkedBlockingQueue
概述
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的更多相关文章
- 11.并发包阻塞队列之LinkedBlockingQueue
在上文<10.并发包阻塞队列之ArrayBlockingQueue>中简要解析了ArrayBlockingQueue部分源码,在本文中同样要介绍的是Java并发包中的阻塞队列LinkedB ...
- java阻塞队列之LinkedBlockingQueue
LinkedBlockingQueue是BlockingQueue中的其中一个,其实现方式为单向链表,下面看其具体实现.(均为JDK8) 一.构造函数 在LinkedBlockingQueue中有三个 ...
- 用Java如何设计一个阻塞队列,然后说说ArrayBlockingQueue和LinkedBlockingQueue
前言 用Java如何设计一个阻塞队列,这个问题是在面滴滴的时候被问到的.当时确实没回答好,只是说了用个List,然后消费者再用个死循环一直去监控list的是否有值,有值的话就处理List里面的内容.回 ...
- Java中的阻塞队列
1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用 ...
- java并发编程学习: 阻塞队列 使用 及 实现原理
队列(Queue)与栈(Stack)是数据结构中的二种常用结构,队列的特点是先进先出(First In First Out),而Stack是先进后出(First In Last Out),说得通俗点: ...
- java并发:阻塞队列
第一节 阻塞队列 1.1 初识阻塞队列 队列以一种先进先出的方式管理数据,阻塞队列(BlockingQueue)是一个支持两个附加操作的队列,这两个附加的操作是:在队列为空时,获取元素的线程会等待队列 ...
- 聊聊并发(七)——Java中的阻塞队列
3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...
- Java并发编程(十二)-- 阻塞队列
在介绍Java的阻塞队列之前,我们简单介绍一下队列. 队列 队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向 ...
- 阻塞队列 BlockingQueue 详解
转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487078&idx=2&sn=315f39b6d53 ...
随机推荐
- SQL 根据身份证号码获取年龄的函数
在数据库的运用过程中,我们时常会碰到根据身份证号码来获取当前的年龄,今天我在这里写了一个函数,就是关于获取年龄的 create or replace function FUNC_COMPARE_SFZ ...
- day007-线程池、Lambda
1. 线程池 就好生活中的游泳池,可以容纳多人在池子里游泳.戏水.线程池里可以有多个正在运行的线程,同时执行已经提交了的多个任务. 什么是线程池?一个用来创建和管理线程的容器. 线程池的原理 线程复用 ...
- 三个方法教会你win7中IIS7配置php环境
三个方法教会你win7中IIS7配置php环境.今天静下心来研究了下在win7中使用IIS7配置php环境,其实很简单!跟下面方法做之前,请先确定你的电脑中未安装其它相关环境程序及服务,之前安装过ap ...
- NO.005-2018.02.10《南歌子词二首 / 新添声杨柳枝词》唐代:温庭筠
南歌子词二首 / 新添声杨柳枝词_古诗文网 南歌子词二首 / 新添声杨柳枝词 唐代:温庭筠(yún) 一尺深红胜曲尘,天生旧物不如新.(胜曲尘 一作:蒙曲尘)一袭深红色的长裙日子久了便蒙上了淡黄色,自 ...
- 构建高性能插件式Web框架
基于MVC插件模式构建支持数据库集群.数据实时同步.数据发布与订阅的Web框架系统.如下图: 1.基于插件式开发 采用插件模式开发的优点是使得系统框架和业务模式有效地进行分离,系统更新也比较简单,只需 ...
- php多进程写入文件
测试一 $begin = time(); for ($i=0; $i<10000; $i++) { $fp = fopen("tmp", 'r+'); fseek($fp, ...
- (第六场)Heritage of skywalkert 【玄学】
题目链接:https://www.nowcoder.com/acm/contest/144/J 标题:J.Heritage of skywalkert | 时间限制:1 秒 | 内存限制:256M s ...
- 2018.10.14 MyBatis配置实现对用户的增删改查
记得导入对应的Jar包 ant-1.9.6.jar ant-launcher-1.9.6.jar asm-5.2.jar cglib-3.2.5.jar commons-logging-1.2.jar ...
- highcharts(前端报表生成)
前端报表技术:使用 JavaScript 生成漂亮图表 百度 echarts: http://echarts.baidu.com/examples.html Funsioncharts : http: ...
- WP | 后台PHP脚本无法修改及服务器无法直接写入问题的解决
问题描述 试图修改页脚的时候,发现WordPress后台Appearance(外观)选项之下没有Editor(编辑)的子选项,之后进行设置修改后又发现无法保存. 试图安装插件,但是无法直接安装,推测是 ...