深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)
1. 引言
在并发编程中我们有时候需要使用线程安全的队列。
如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法。
使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,
而非阻塞的实现方式则可以使用循环CAS的方式来实现,本文让我们一起来研究下如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的。
2. ConcurrentLinkedQueue的介绍
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,
当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。
入队列就是将入队节点添加到队列的尾部。
tail.casNext(
null
, n) CAS算法,往tail的Next 设入队节点n。
tail.casTail(tail, n) CAS算法,把入队节点n 标记为tail 。
01 |
public boolean offer(E e) { |
02 |
03 |
if (e == null ) |
04 |
05 |
throw new NullPointerException(); |
06 |
07 |
Node</e><e> n = new Node</e><e>(e); |
08 |
09 |
for (;;) { |
10 |
11 |
Node</e><e> t = tail; |
12 |
13 |
if (t.casNext( null , n) && casTail(t, n)) { |
14 |
15 |
return true ; |
16 |
17 |
} |
18 |
19 |
} |
20 |
21 |
} |
这个方法没有任何锁操作。线程安全完全由CAS操作和队列的算法来保证。整个算法的核心是for循环,这个循环没有出口,直到尝试成功,这也符合CAS操作的流程。
ConcurrentLinkedQueue使用时,应当注意的地方:
查看ConcurrentLinkedQueue的API ,
.size() 是要遍历一遍集合的,性能慢,所以尽量要避免用size而改用 isEmpty()
ConcurrentLinkedQueue并发队列的一个demo:
先建立一个测试pojo,Log.java两个字段,加上get、set方法
private Date date;
private String value;
然后建立队列类,如下:
/**
* 用于记录日志的队列,ConcurrentLinkedQueue <br/>
* 此队列按照 FIFO(先进先出)原则对元素进行排序,详见J2SE_API或JDK
* @author RSun
* 2012-2-22下午05:05:19
*/
public class SystemLogQueue {
private static Queue<Log> log_Queue;
static{
if (null == log_Queue) {
log_Queue = new ConcurrentLinkedQueue<Log>(); //基于链接节点的无界线程安全队列
}
}
/** 初始化创建队列 **/
public static void init() {
if (null == log_Queue) {
log_Queue = new ConcurrentLinkedQueue<Log>(); //基于链接节点的无界线程安全队列
}
}
/**
* 添加到队列方法,将指定元素插入此队列的尾部。
* @param log Log对象
* @return 成功返回true,否则抛出 IllegalStateException
*/
public static boolean add(Log log) {
return (log_Queue.add(log));//由于是无界队列(21亿个元素),基本上可以保证一直添加
}
/** 获取并移除此队列的头 ,如果此队列为空,则返回 null */
public static Log getPoll() {
return (log_Queue.poll());
}
/** 获取但不移除此队列的头;如果此队列为空,则返回 null **/
public static Log getPeek() {
return (log_Queue.peek());
}
/** 判断此队列是否有元素 ,没有返回true **/
public static boolean isEmpty() {
return (log_Queue.isEmpty());
}
/** 获取size,速度比较慢 **/
public static int getQueueSize() {
return (log_Queue.size());
}
public static void main(String[] args) {
System.out.println("队列是否有元素:" + !isEmpty());
Log log = new Log();
log_Queue.add(log);
System.out.println("队列是否有元素:" + !isEmpty());
Log log2 = new Log();
log2.setDate(new Date());
log2.setValue("哈哈哈");
log_Queue.add(log2);
System.out.println("队列元素个数:" + getQueueSize());
Log l = getPeek();
System.out.println("\n获取队列数据:" + l.getValue() + "---" + l.getDate());
System.out.println("队列元素个数:" + getQueueSize());
for (int i = 0; i < 2; i++) {
Log l2 = getPoll();
if(l2 != null){
System.out.println("\n获取队列数据并删除:" + l2.getValue() + "---" + l2.getDate());
}
System.out.println("队列元素个数:" + getQueueSize());
}
结果:
// 队列是否有元素:false
// 队列是否有元素:true
// 队列元素个数:2
// 获取队列数据:null---null
// 队列元素个数:2
// 获取队列数据并删除:null---null
// 队列元素个数:1
// 获取队列数据并删除:哈哈哈---Thu Nov 15 13:58:02 CST 2012
// 队列元素个数:0
}
}
深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)的更多相关文章
- 深入理解java:2.3.5. 并发编程concurrent包 之容器BlockingQueue(阻塞队列)
1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空. 当队列满时,存储元素的线程会等待队列 ...
- 深入理解java:2.3.3. 并发编程concurrent包 之容器ConcurrentHashMap
线程不安全的HashMap 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. 效率低下的HashTable容器 H ...
- Python并发编程-concurrent包
Python并发编程-concurrent包 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.concurrent.futures包概述 3.2版本引入的模块. 异步并行任务编程 ...
- 深入理解java:2.3.2. 并发编程concurrent包 之重入锁/读写锁/条件锁
重入锁 Java中的重入锁(即ReentrantLock) 与JVM内置锁(即synchronized)一样,是一种排它锁. ReentrantLock提供了多样化的同步,比如有时间限制的同步(定 ...
- java多线程 --ConcurrentLinkedQueue 非阻塞 线程安全队列
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部:当我们获取一个元素时,它会返回队列头 ...
- 深入理解java:2.3.6. 并发编程concurrent包 之管理类---线程池
我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁 ...
- 深入理解java:2.3.1. 并发编程concurrent包 之Atomic原子操作(循环CAS)
java中,可能有一些场景,操作非常简单,但是容易存在并发问题,比如i++, 此时,如果依赖锁机制,可能带来性能损耗等问题, 于是,如何更加简单的实现原子性操作,就成为java中需要面对的一个问题. ...
- 并发编程:协程TCP、非阻塞IO、多路复用、
一.线程池实现阻塞IO 二.非阻塞IO模型 三.多路复用,降低CPU占用 四.模拟异步IO 一.线程池实现阻塞IO 线程阻塞IO 客户端 import socket c = socket.socket ...
- 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念
volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...
随机推荐
- Android APK 手动签名
首先,如果没有签名密钥,先生成密钥: keytool -genkey -alias android.keystore -keyalg RSA -validity 20000 -keystore and ...
- 解决supervisord启动问题
作者:StormerX链接:https://www.jianshu.com/p/d8901ce4712b来源:简书简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处. $ superv ...
- Acwing-100-IncDec序列(差分)
链接: https://www.acwing.com/problem/content/102/ 题意: 给定一个长度为 n 的数列 a1,a2,-,an,每次可以选择一个区间 [l,r],使下标在这个 ...
- 【Winform-ComboBox】实现ComboBox下拉框与数据库的绑定
实现效果如下: 1.设计窗体 下拉框的名称cmbName 2.连接数据库 DBHelper类代码: class DBHelper { /// <summary> /// 创建静态连接字符串 ...
- jquery keydown()方法 语法
jquery keydown()方法 语法 作用:完整的 key press 过程分为两个部分:1. 按键被按下:2. 按键被松开.当按钮被按下时,发生 keydown 事件.keydown() 方法 ...
- php上传大文件1G
前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...
- 51 Nod 1086 多重背包问题(单调队列优化)
1086 背包问题 V2 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 有N种物品,每种物品的数量为C1,C2......Cn.从中任选若干件放 ...
- 51 Nod 线段最长重叠部分
1091 线段的重叠 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 X轴上有N条线段,每条线段包括1个起点和终点.线段的重叠是这样来算的,[10 20]和[12 ...
- Springboot 解决跨域请求
Cors处理 跨域请求 细粒度 直接在controller层上 添加@CrossOrigin注解 @PostMapping("/") @CrossOrigin(value = &q ...
- html密码框value为空,但是总有默认密码(原)
input输入框加属性:autocomplete="new-password" ,浏览器就不会给他填充默认密码. <input class="form-contro ...