阻塞队列之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 ...
随机推荐
- Python学习系列提升篇------字符串
字符串是python学习中重要的内容,在以后的工作中,对字符串的处理也必少不了.下面总结一下关于字符串学习的经验. 1.1 字符串的连接和合并 用‘ + ’连接,将两个字符串相加. 合并, ...
- greenplum维护
1.用户管理 psql -d sea CREATE DATABASE BI; CREATE USER ubi WITH PASSWORD 'pwdbi' NOSUPERUSER; GRANT ALL ...
- 別人寫的git的總結,寫自己這裡學習用
這裡是原文,http://www.cnblogs.com/ang-/p/7352909.html 貼這裡慢慢學. git入门大全 阅读目录 前言 基本概念 文件几种状态 创建新仓库 配置 检出仓库 ...
- 转载: Centos7 升级python3,解决升级后不兼容问题
Centos7配置更新国内yum源 http://blog.csdn.net/qingguiyu/article/details/50721956 Centos7 升级python3,解决升级后不兼容 ...
- Master HA彻底解密
本课主题 Master HA 解析 Master HA 解析源码分享 Master HA 解析 生产环境下一般采用 ZooKeeper 做 HA,且建义为 3台 Master, ZooKeeper 会 ...
- mysqlimport导入简单测试
1 创建一个文本文档:[mysql@xxxycrdb]$ more /tmp/ldcmd1.txt 1,abc,abc@qq.com1,abc,abc@qq.com1,abc,abc@qq.co ...
- SQL server 2008 安装报错 reporting services catalog database file existence
SQL server 2008 安装时报错 提示 reporting services catalog database file existence 查了一下,是因为原来装过Sql server 2 ...
- Kubernetes API server工作原理
作为Kubernetes的使用者,每天用得最多的命令就是kubectl XXX了. kubectl其实就是一个控制台,主要提供的功能: 1. 提供Kubernetes集群管理的REST API接口,包 ...
- windows网络模型之重叠IO(完成例程)的使用
#include <WINSOCK2.H> #include <stdio.h> #define PORT 5150 #define MSGSIZE 1024 #pragma ...
- POSIX多线程—线程基本概念
http://blog.csdn.net/livelylittlefish/article/details/7957007 作者:阿波链接:http://blog.csdn.net/livelylit ...