阻塞队列之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 ...
随机推荐
- 我的书单mybooklist
首先自我介绍一下,我是一名程序员. 计算机的世界太浩瀚,而我太过渺小. 比计算机的世界更为广大的是,书籍的海洋.学海无涯. 无论是计算机的世界,还是其他类书的世界,人一辈子要看的书看也看不完. 于是我 ...
- 内存分配malloc函数注意事项。
malloc的全称是memory allocation,中文叫动态内存分配,用于向系统申请分配指定字节的内存空间 原型:extern void *malloc(unsigned int num_byt ...
- GridCellChoiceEditor
choice_editor = wx.grid.GridCellChoiceEditor(choices_list, True) grid.SetCellEditor(row, col, choice ...
- web.xml中不同版本的servlet头以及版本控制
参考文章: http://www.cnblogs.com/beijingstruggle/p/5461146.html http://blog.csdn.net/z69183787/article/d ...
- constructor()方法
在做微信小程序的时候,需要对传输的数据进行加密,大牛给我介绍constructor()方法,不是很懂这个但是用了一次,今天来用自己的想法来理解这个方法 ———————————————————————— ...
- 大数据的正确用法你get到了吗?
Azure 镜像市场已于2016年9月21日正式上线,在这个统一的集成平台中,客户可以轻松地浏览.搜索和选择一系列来自第三方的应用和解决方案,并可以将其快速一键部署到 Azure 实例当中. 在移动为 ...
- Visual Studio 快捷键汇总
常见方法: 强迫智能感知:Ctrl+J.智能感知是Visual Studio最大的亮点之一,选择Visual Studio恐怕不会没有这个原因. 撤销:Ctrl+Z.除非你是天才,那么这个快捷键也是 ...
- JavaScript类型操作以及一些规范
类型检测 类型检测优先使用 typeof.对象类型检测使用 instanceof.null 或 undefined 的检测使用 == null. // string typeof variable = ...
- PHP腾讯与百度坐标转换
function coordinate_switch($a,$b){//百度转腾讯坐标转换 $a = Latitude , $b = Longitude $x = (double)$b - 0.006 ...
- PS软件怎么把视频转成gif动态图?
PS软件怎么把视频转成gif动态图?Adobe PhotoShop软件的最新版本是可以编辑视频的,并且可以将视频转换为GIF动态图,使用也很简单,下面分享ps制作gif动态图的教程,需要的朋友可以参考 ...