java轻松实现无锁队列

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。

1
2
3
4
5
6
7
int compare_and_swap (int* reg, int oldval, int newval)
{
  int old_reg_val = *reg;
  if (old_reg_val == oldval)
     *reg = newval;
  return old_reg_val;
}

用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

numpy数组按某一维度相加_Python数据分析之NumPy(高级篇)

​一些更高级的ndarray处理where和一些其他的逻辑运算np.where(cond,x,y)&#xff1a;满足条件(cond)输出x&#xff0c;不满足输出yx_arr np.array([1.1, 1.2, 1.3, 1.4, 1.5])y_arr np.array([2.1, 2.2, 2.3, 2.4, 2.5])cond np.array([True, False, True, True, False])pr…

md5与des算法有何不同_到底AI芯片和传统芯片有何区别?

前两天成立仅两年国内专做人工智能FPGA加速算法的初创公司深鉴科技被国际巨头赛灵思收购了&#xff0c;在业界引起不小的震动。目前国内做AI芯片的公司可谓不少了&#xff0c;AI芯片已然成为了当下芯片行业最热领域。但是大部分人对AI芯片的架构应该都不是太了解。那么AI 芯片和…

Active Directory PowerShell模块收集AD信息

0x00 前言简介 Microsoft为Windows Server 2008 R2&#xff08;以及更高版本&#xff09;提供了多个Active Directory PowerShell cmdlet&#xff0c;这大大简化了以前需要将涉及到的ADSI冗长代码行放在一起的任务。 在Windows客户端上&#xff0c;需要安装远程服务器管理工具&…

anaconda对应python版本_Python基础——如何查看python版本、如何查看多个python版本

前言初学者来说&#xff0c;安装python过程是存在一定难度的。在安装过程中&#xff0c;可能安装了多个python版本&#xff0c;可能安装了anaconda导致有自带的python&#xff0c;同时本身电脑也安装了官方下载的python也茫然不知。导致可能有以下情况发生&#xff1a;1.pip in…

MATLAB统计与回归

11.1 前言統計的技巧與資料分析常常形影不離。一般統計使用加法、累加法、平均值&#xff0c;中間值等等&#xff0c;由於處理的對象是矩陣資料&#xff0c;故其基本統計之技巧已經廣為應用&#xff0c;其觀念也會在正常之運作中出現。統計學中比較特殊應用者為機率、亂數、常態…

yii2通过url访问类中的方法_每日学点---nginx变量使用方法详解(3)

也有一些内建变量是支持改写的&#xff0c;其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分&#xff0c;如果有的话 )&#xff0c;而在赋值时可以直接修改参数串。我们来看一个例子&#xff1a;location /test { set $orig_args…

python语言format用法_详解Python中的format格式化函数的使用方法

format函数实现字符串格式化的功能 基本语法为&#xff1a; 通过 : 和 {} 来控制字符串的操作 一、对字符串进行操作 1. 不设置指定位置&#xff0c;按默认顺序插入 ①当参数个数等于{}个数的时候 str_1 "小明{}小美,可是小美{}小明".format("喜欢", &quo…

ps安装了可以打开但开始里面找不到_PS2018打开了钢笔压力但却没有压感的解决方法...

1.首先应确定是否安装数位板的驱动&#xff0c;如果驱动出现问题也可以试着重装一下。2.&#xff08;这里以Photoshop CC 2018为例&#xff09;接下来检查这个“始终对‘大小’使用‘压力’”按钮是否打开&#xff0c;如果是关闭的&#xff0c;试着打开。3.随后F5进入“画笔”选…

t检验的p值对照表_论文数据分析实战 | 如何对汇总数据进行t检验

在SPSS统计分析交流群中有学员在阅读论文的过程中看到下面的这张表格&#xff1a;这张表中记录了第16届世界男子篮球锦标赛中国队与前8名球队进攻指标比较的结果&#xff0c;其中这份表格并没有给出详细的P值&#xff0c;而只是告诉我们P值小于多少。在这种只有汇总数据&#x…

for命令不跳过空白行_Java程序员必备:查看日志常用的linux命令

iwenhou趁周末&#xff0c;复习一下鸟哥的linux私房菜&#xff0c;看了文件内容查阅部分&#xff0c;做个笔记&#xff0c;哈哈&#xff0c;希望对你有帮助哦。catcat : 由第一行开始显示文件所有内容参数说明cat [-AbEnTv] 参数&#xff1a; -A : 相当于-vET 的整合参数&#…

Java并发编程笔记之Semaphore信号量源码分析

JUC 中 Semaphore 的使用与原理分析&#xff0c;Semaphore 也是 Java 中的一个同步器&#xff0c;与 CountDownLatch 和 CycleBarrier 不同在于它内部的计数器是递增的&#xff0c;那么&#xff0c;Semaphore 的内部实现是怎样的呢&#xff1f; Semaphore 信号量也是Java 中一个…

设计模式--命令模式

实验16&#xff1a;命令模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解命令模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用命令模式解决实际问题。 [实验任务]&#xff1a;多次撤销和重复的命令模式 某系…

wxpython界面切换_Python图形界面开发—wxPython库的布局管理及页面切换

前言 wxPython是基于Python的跨平台GUI扩展库&#xff0c;对wxWidgets&#xff08; C 编写&#xff09;封装实现。GUI程序的开发中界面布局是很重要的一个部分&#xff0c;合理的页面布局能够给予用户良好使用体验。虽然在GUI的控件和窗口布局上可以使用坐标&#xff0c;但更多…

javah找不到类文件

这样即可&#xff0c;在src目录下寻找类&#xff0c;类要写全&#xff0c;即包名.类名 转载于:https://www.cnblogs.com/Java-Starter/p/9283830.html

python卸载opencv_20.Windows python,opencv的安装与卸载

本机Windows7系统&#xff0c;之前安装了Python3.5的版本&#xff0c;后来因为要装opencv&#xff0c;而opencv只支持Python2.7的版本&#xff0c;所以需要将Python3.5进行卸载。在控制面板-卸载程序&#xff0c;将Python卸载后删除其注册表等残留文件 下载Python2.7&#xff0…

LinuX 硬盘分区细节详谈 【 整理至 LinuxSir BY FreeXploiT 】

系统引导过程及硬盘分区结构论述作者&#xff1a; zhy2111314来自&#xff1a; LinuxSir.Org ouc.edu.cn摘要&#xff1a; 本文是理论性文档&#xff0c;主要讲述系统引导过程以及硬盘的物理结构&#xff1b;正文一、系统引导过程简介系统引导过程主要由以下几个步骤组成(以硬盘…

破解WEP密钥过程全解(上)

WLAN技术出现之后&#xff0c;“安全”就成为始终伴随在“无线”这个词身边的影子&#xff0c;针对无线网络技术中涉及的安全认证加密协议的攻击与破解就层出不穷。现在&#xff0c;因特网上可能有数以百计&#xff0c;甚至以千计的文章介绍关于怎么攻击与破解WEP&#xff0c;但…

bfc是什么_一次弄懂css的BFC

前言BFC在css的学习中是重要的但不易理解的概念&#xff0c;BFC也牵扯了很多其他问题&#xff0c;如浮动、定位、盒模型等&#xff0c;因此弄懂BFC是很有必要的。本文对BFC进行总结&#xff0c;希望对你有所帮助。BFC是什么&#xff1f;先看看MDN的定义&#xff1a;块格式化上下…

ES6模块的import和export用法总结

ES6之前已经出现了js模块加载的方案&#xff0c;最主要的是CommonJS和AMD规范。commonjs主要应用于服务器&#xff0c;实现同步加载&#xff0c;如nodejs。AMD规范应用于浏览器&#xff0c;如requirejs&#xff0c;为异步加载。同时还有CMD规范&#xff0c;为同步加载方案如sea…

windows网关详解 【了解网关的重要性,增加网络性能】【FreeXploiT综合文】

理解Windows中的路由表和默认网关 每一个Windows系统中都具有IP路由表&#xff0c;它存储了本地计算机可以到达的网络目的地址范围和如何到达的路由信息。路由表是TCP/IP通信的基础&#xff0c;本地计算机上的任何TCP/IP通信都受到路由表的控制。 理解路由表 你可以运行 route …