无锁队列java实现_java轻松实现无锁队列
深入解析java java虚拟机jvm编译器
348.5元
(需用券)
去购买 >
1、什么是无锁(Lock-Free)编程
当谈及 Lock-Free 编程时,我们常将其概念与 Mutex(互斥) 或 Lock(锁) 联系在一起,描述要在编程中尽量少使用这些锁结构,降低线程间互相阻塞的机会,以提高应用程序的性能。类同的概念还有 "Lockless" 和 "Non-Blocking" 等。实际上,这样的描述只涵盖了 Lock-Free编程的一部分内容。本质上说,Lock-Free 编程仅描述了代码所表述的性质,而没有限定或要求代码该如何编写。
基本上,如果程序中的某一部分符合下面的条件判定描述,则我们称这部分程序是符合 Lock-Free的。反过来说,如果某一部分程序不符合下面的条件描述,则称这部分程序是不符合 Lock-Free 的。
上面的英文翻译成中文就是很简单的:如果你的应用程序是多线程并且它们之间都有访问共享内存但是访问时并没有相互阻塞,那它就是lock-free编程。注意lock-free只是强调了编程概念并没指定其具体的实现形式,其强调的概念是「线程间访问共享内存时不会相互阻塞」。那如果没有lock或者Mutex就一定是lock-free编程了吗,看下面的代码片段:
x = 0;
while(x == 0){
x = 1 - x;
}
假设有线程T1,T2同时调用这段代码,T1,T2都判断x == 0,进行到循环。T1先执行 x = 1 - 0,此时 x = 1后 T2 执行 x = 1 - 1。x = 0。T1,T2此时判断x == 0,结果两者又进入了循环。。。线程T1,T2相互影响,两者都陷入了死循环,这种某种意义也算得上是相互阻塞使线程,所以这不算是lock-free编程。
ok,了解了lock-free编程的相关概念那要怎么实现呢。在开始说无锁队列之前,我们需要知道一个很重要的技术就是CAS操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。有了这个原子操作,我们就可以用其来实现各种无锁(lock free)的数据结构。
这个操作用C语言来描述就是下面这个样子:意思就是说,看一看内存*reg里的值是不是oldval,如果是的话,则对其赋值newval。
用JAVA语言则是:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
了解了CAS操作之后实现lock-free数据结构思路是怎样呢?这里就有篇论文讲述了思路:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&rep=rep1&type=pdf。其中里面就提到了如何用数组实现一个lock-free队列。有兴趣的朋友可以参考上面链接阅读里面的第5章节。现在说一下我自己具体的实现思路:
数组队列是一个循环数组,队列少用一个元素,当头等于尾标示队空,尾加1等于头标示队满。
数组的元素用EMPTY(无数据,标示可以入队)和FULL(有数据,标示可以出队)标记指示,数组一开始全部初始化成 EMPTY标示空队列。
EnQue 操作:如果当前队尾位置为EMPTY,标示线程可以在当前位置入队,通过CAS原子操作把该位置设置为FULL,避免其它线程操作这个位置,操作完后修改队尾位置。各个线程竞争新的队尾位置。如下图所示:
下面是贴上具体的代码:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* 用数组实现无锁有界队列
*/
public class LockFreeQueue {
private AtomicReferenceArray atomicReferenceArray;
//代表为空,没有元素
private static final Integer EMPTY = null;
//头指针,尾指针
AtomicInteger head,tail;
public LockFreeQueue(int size){
atomicReferenceArray = new AtomicReferenceArray(new Integer[size + 1]);
head = new AtomicInteger(0);
tail = new AtomicInteger(0);
}
/**
* 入队
* @param element
* @return
*/
public boolean add(Integer element){
int index = (tail.get() + 1) % atomicReferenceArray.length();
if( index == head.get() % atomicReferenceArray.length()){
System.out.println("当前队列已满,"+ element+"无法入队!");
return false;
}
while(!atomicReferenceArray.compareAndSet(index,EMPTY,element)){
return add(element);
}
tail.incrementAndGet(); //移动尾指针
System.out.println("入队成功!" + element);
return true;
}
/**
* 出队
* @return
*/
public Integer poll(){
if(head.get() == tail.get()){
System.out.println("当前队列为空");
return null;
}
int index = (head.get() + 1) % atomicReferenceArray.length();
Integer ele = (Integer) atomicReferenceArray.get(index);
if(ele == null){ //有可能其它线程也在出队
return poll();
}
while(!atomicReferenceArray.compareAndSet(index,ele,EMPTY)){
return poll();
}
head.incrementAndGet();
System.out.println("出队成功!" + ele);
return ele;
}
public void print(){
StringBuffer buffer = new StringBuffer("[");
for(int i = 0; i < atomicReferenceArray.length() ; i++){
if(i == head.get() || atomicReferenceArray.get(i) == null){
continue;
}
buffer.append(atomicReferenceArray.get(i) + ",");
}
buffer.deleteCharAt(buffer.length() - 1);
buffer.append("]");
System.out.println("队列内容:" +buffer.toString());
}
}
代码很简单,相应的注释也写上了,相信大家都应该看得懂~。
这里说明一下JDK提供的CAS原子操作类都位于 java.util.concurrent.atomic下面。这里用到的是数组我用的是AtomicReferenceArray类,当然你也可以用AtomicIntegerArray。这里用到了两个原子类的作为指针head,tail,利用mod队列的长度来实现一个循环数组。
下面测试我们的代码:
import java.util.stream.IntStream;
public class LockFreeDemo {
public static void main(String[] args) {
LockFreeQueue queue = new LockFreeQueue(5);
IntStream.rangeClosed(1, 10).parallel().forEach(
i -> {
if (i % 2 == 0) {
queue.add(i);
} else {
queue.poll();
}
}
);
queue.print();
}
}
这里面用了JDK8的lambda并行流的特性,起了Ncpu线程去并发得入队和出队。运行结果如下:
入队成功!2
当前队列为空
当前队列为空
入队成功!6
当前队列为空
入队成功!8
出队成功!2
入队成功!10
出队成功!6
入队成功!4
队列内容:[4,8,10]
因为是并发打印,所以打出来的信息整体是无序的,但是对于同一个元素的操作,我们看到是相对有序的~
原文链接:https://www.cnblogs.com/linlinismine/p/9263426.html
java 11官方入门(第8版)教材
79.84元
包邮
(需用券)
去购买 >
无锁队列java实现_java轻松实现无锁队列相关推荐
- java 队列已满_JAVA中常见的阻塞队列详解
在之前的线程池的介绍中我们看到了很多阻塞队列,这篇文章我们主要来说说阻塞队列的事. 阻塞队列也就是 BlockingQueue ,这个类是一个接 口,同时继承了 Queue 接口,这两个接口都是在JD ...
- java 无符号转有符号_java有符号无符号的转换
数据处理中常常遇到基本数据类型的操作,java都是有符号的数据,而与下位机通信中常常遇到无符号的比如uint8, uint16,uint32等等 1.为了完成这个功能还专门采用ByteBuffer的方 ...
- 阻塞队列 java实现_JAVA实现阻塞队列
package 多线程并发; import java.util.Stack; /** * Created by z84102272 on 2018/7/17. */ public class Bloc ...
- java 队列和堆栈_Java中的堆栈和队列
java 队列和堆栈 我最近一直在研究一些需要堆栈和队列的Java代码. 使用的选择不是立即显而易见的. 有一个Queue接口,但没有明确的具体实现要使用. 还有一个Stack类,但是javadocs ...
- java 并发_Java并发原理无废话指南
专注于Java领域优质技术,欢迎关注 作者: 写程序的康德 网上有不计其数的并发编程文章,甚至有不计其数的书来介绍这个主题.你为什么要花10分钟时间来读完这篇文章呢?我给的答案:"他们全是废 ...
- java可重入锁是什么意思_Java中的可重入(Reentrant)锁
## 什么是可重入锁? 可重入锁是一种特殊的互斥锁,它可以被同一个线程多次获取,而不会产生死锁. 1. 首先它是互斥锁:任意时刻,只有一个线程锁.即假设A线程已经获取了锁,在A线程释放这个锁之前,B线 ...
- java让线程空转_Java锁:悲观/乐观/阻塞/自旋/公平锁/闭锁,锁消除CAS及synchronized的三种锁级别...
JAVA LOCK 大全 [TOC] 一.广义分类:乐观锁/悲观锁 1.1 乐观锁的实现CAS (Compare and Swap) 乐观锁适合低并发的情况,在高并发的情况下由于自旋,性能甚至可能悲观 ...
- java中的账户冻结原理_java可重入锁(ReentrantLock)的实现原理
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
- java线程钥匙_Java多线程并发编程/锁的理解
一.前言 最近项目遇到多线程并发的情景(并发抢单&恢复库存并行),代码在正常情况下运行没有什么问题,在高并发压测下会出现:库存超发/总库存与sku库存对不上等各种问题. 在运用了 限流/加锁等 ...
最新文章
- 【机器学习入门】(12) 特征工程:特征选择、数据降维、PCA
- 配置Groovy开发运行环境
- Windows核心编程 第六章 线程基础知识 (下)
- 经典网页设计:15个使用网格系统的精美网站作品
- padavan 源码
- 数据结构(6) -- 查找
- 前端学习(1760):前端调试值之如何让浏览器阻止请求相关资源
- mysql运行效率最高archive_MySQL 的优化方案总结
- Linux之python3编译安装
- [转载] 两种方法分割python多空格字符串
- 数据库Sharding的基本思想和切分策略(转)
- Mycat的使用 - 03.全局序列号
- oracle拼音匹配,求完整简洁的Oracle获得汉字字符串拼音首字母和全拼的函数
- 各大主流编程语言性能PK,结果出乎意料
- 利用gsm模块自动收发短信
- 天才小毒妃 第945章 龙非夜心情很不好
- 18个好用APP,你手机里有哪些堪称神器的APP
- 在计算机术语中 将ALU控制器和,计算机组成原理试管理-题集
- Wr720n改装OpenWrt打印服务器实现网络无线打印
- 移动端电影院:享受触手可及的幸福
热门文章
- 数字信号处理音频FIR去噪滤波器(基于MATLAB GUI的开发)
- Java三大异常概念和处理步骤(防患于未然)
- bugku 求getshell
- 放在github pages上的静态网站怎么取消绑定自定义域名?
- IBM X3650 服务器使用SERVERAID 8K-1做RAID1更换硬盘
- 冈萨雷斯DIP第8章知识点
- Python使用Excel表格的学生成绩管理系统
- ssm框架sql换成MySQL_SSM框架之mybatis
- IOS视频直播:高仿腾讯旗下NOW直播映客直播类型
- JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配