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

java轻松实现无锁队列相关推荐

  1. 无锁队列java实现_java轻松实现无锁队列

    深入解析java java虚拟机jvm编译器 348.5元 (需用券) 去购买 > 1.什么是无锁(Lock-Free)编程 当谈及 Lock-Free 编程时,我们常将其概念与 Mutex(互 ...

  2. java实现无锁队列

    写作目的 说到无锁,其实就是用cas,不过我在百度上搜java实现无锁队列的文章其实不多,所以自己用cas和volatile实现一下,线程安全那是必须的. 无锁队列 package untils;im ...

  3. java 无锁队列实现_java无锁队列实现

    对于像应用中多个生产者需要并发发送一些日志信息给远程存储服务器,这些日志信息用于dubbo的调用链分析. 一种方案是生产者线程将要发送的日志消息存储到队列当中,然后由另一个本地消费线程从队列中获取要发 ...

  4. 你应该知道的高性能无锁队列Disruptor

    1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...

  5. 原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析

    原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析 - 一.大致介绍 1.了解过netty原理的童鞋,其实应该知道工作线程组的每个子线程都维护了一个任 ...

  6. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  7. CAS无锁队列的实现

    文章目录 1. 基本原理 2. 代码实现 2.1 使用链表实现无锁队列 2.2 使用数组实现环形无锁队列 3. ABA 问题及解决 4. 参考资料 1. 基本原理 源于1994年10月发表在国际并行与 ...

  8. 无锁队列Disruptor

    1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...

  9. 基于数组的无锁队列(译)

    2019独角兽企业重金招聘Python工程师标准>>> 1 引言 最近对于注重性能的应用程序,我们有了一种能显著提高程序性能的选择:多线程.线程的概念实际上已经存在了很长时间.在过去 ...

最新文章

  1. eclipse编辑窗口不见了(打开左边的java、xml文件,中间不会显示代码)
  2. 有了四步解题法模板,再也不害怕动态规划!
  3. android 类加载器 DexClassLoader的用法,以及引出的插件架构
  4. 第3章 StringBuilder类
  5. NOI2014 动物园
  6. java获取环境路径方法_JAVA获取服务器路径的方法
  7. Openstack 与VMware 不同CPU迁移原理
  8. 安卓APP_ 布局(4) —— TableLayout表格布局
  9. NHibernate学习导航
  10. 那些年黑了你的微软BUG
  11. python画卡通皮卡丘_实现童年Pokémon,教你用Python画一只属于自己的皮卡丘
  12. python求最大值代码的方式_python使用分治法实现求解最大值的方法
  13. python编程英语单词怎么写_用Python写一个背英文单词程序
  14. 2. MarkText可代替Typora的markdown 编辑器
  15. NYOJ--41--三个数从小到大排序
  16. excel函数学习系列一
  17. 【Flutter】微信项目实战【01】基本框架搭建
  18. [长文科普]浅谈数据湖的应用与安全
  19. mysql 图文安装_mysql安装图解mysql图文安装教程(详细说明)
  20. chap1统计学习及监督学习

热门文章

  1. oracle数据倾斜优化,Hive数据倾斜优化 - ericquan8的个人页面 - OSCHINA - 中文开源技术交流社区...
  2. axios (get post请求、头部参数添加)傻瓜式入门axios
  3. GIT提交的时候出现 ! [rejected] master -> master (non-fast-forward)错误
  4. ajax success function_Django:AJAX(二)
  5. python怎么输出结果_Python中print()常用输出方法
  6. Mysql--重点1
  7. xshell报编码问题时可以修改xshell编码
  8. 解决ListView 缓存机制带来的显示不正常问题
  9. excel按季度分类汇总_Excel数据分析实战(1)--电商销售记录分析
  10. Java 面试题(3)—— JVM