java实现无锁队列

java实现无锁队列okimportjava util Random importjava util concurrent ExecutorServ importjava util concurrent Executors importjava util concurrent TimeUnit importjava util concurrent atomic AtomicIntege importjava util concurrent atomic AtomicRefere

写作目的

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

无锁队列

package untils; import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicInteger; import lombok.Data; import sun.misc.Unsafe; / * Created on 2021-06-23 */ public class NoLockQueue { private static final Unsafe unsafe; //头节点 volatile Node head; //尾节点 volatile Node tail; //头节点偏移量 private static final Long headOffset; //尾节点偏移量 private static final Long tailOffset; //当前队列的长度 private AtomicInteger length = new AtomicInteger(0); //队列允许的最大长度 private int maxSize = 0; static { try { //获取成员变量 Field field = Unsafe.class.getDeclaredField("theUnsafe"); //设置为可访问 field.setAccessible(true); //是静态字段,用null来获取Unsafe实例 unsafe = (Unsafe) field.get(null); //设置头节点变量在类中的偏移值 headOffset = unsafe.objectFieldOffset(NoLockQueue.class.getDeclaredField("head")); //设置尾节点变量在类中的偏移值 tailOffset = unsafe.objectFieldOffset(NoLockQueue.class.getDeclaredField("tail")); } catch (Exception e) { System.out.println(e.getLocalizedMessage()); throw new Error(e); } } public NoLockQueue() { this.maxSize = Integer.MAX_VALUE; //初始化节点 head = tail = new Node(); } public NoLockQueue(int maxSize) { this.maxSize = maxSize; //初始化节点 head = tail = new Node(); } / * 入队 */ public void enQueue(int value) { //创建新节点 Node newNode = new Node(); newNode.setValue(value); while (true) { //获取尾节点 Node oldTail = this.tail; if (length.get() < maxSize && oldTail.casNext(null, newNode)) { System.out.println(Thread.currentThread().getName() + "进队列:" + value); unsafe.compareAndSwapObject(this, tailOffset, oldTail, newNode); length.incrementAndGet(); break; } } } / * 出队 */ public void dequeue() { while (true) { //如果没有数据 if (length.get() <= 0) { continue; } //获取头节点 Node oldHead = this.head; Node oldNext = oldHead.getNext(); if (unsafe.compareAndSwapObject(this, headOffset, head, oldNext)) { System.out.println(Thread.currentThread().getName() + "出队列:" + head.getValue()); length.decrementAndGet(); break; } } } } @Data class Node { //Unsafe类 private static final Unsafe unsafe; //next变量的偏移量 private static final Long nextOffset; //值 private volatile int value; //next节点 private volatile Node next; static { try { //获取成员变量 Field field = Unsafe.class.getDeclaredField("theUnsafe"); //设置为可访问 field.setAccessible(true); //是静态字段,用null来获取Unsafe实例 unsafe = (Unsafe) field.get(null); //获取state变量在类中的偏移值 nextOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("next")); } catch (Exception e) { System.out.println(e.getLocalizedMessage()); throw new Error(e); } } / * cas的方式设置next的值 * @param before 期望的值 * @param after 修改的值 * @return */ public boolean casNext(Node before, Node after) { return unsafe.compareAndSwapObject(this, nextOffset, before, after); } } 

测试类

package untils; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; / * Created on 2021-06-23 */ public class Main { public static void main(String[] args) { NoLockQueue queue = new NoLockQueue(10); ExecutorService executorService = Executors.newFixedThreadPool(20); Random random = new Random(); for (int i = 0; i < 10; i++) { executorService.submit(new Runnable() { @Override public void run() { queue.enQueue(random.nextInt()); } }); } // //判断入队的顺序 // System.out.println("----------------"); // try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } finally { } // Node p = queue2.head.getNext(); // while (p != null){ // System.out.println(p.getValue()); // p = p.getNext(); // } // // System.out.println("----------------"); for (int i = 0; i < 10; i++) { executorService.submit(new Runnable() { @Override public void run() { queue.dequeue(); } }); } try { TimeUnit.SECONDS.sleep(5); } catch (Exception e) { e.printStackTrace(); } finally { } executorService.shutdown(); } } 

小插曲

unsafe类的获取

其实当时参考的是AtomicInteger里获取unsafe方法

private static final Unsafe unsafe = Unsafe.getUnsafe();

但是报错了,报错的原因竟然是 双亲委派模型

关于通过Unsafe.getUnsafe()方法拿Unsafe对象抛出SecurityException异常的原因 - 言午12138 - 博客园

那怎么获取unsafe类呢,如下所示,固定格式

//获取成员变量 Field field = Unsafe.class.getDeclaredField("theUnsafe"); //设置为可访问 field.setAccessible(true); //是静态字段,用null来获取Unsafe实例 Unsafe unsafe = (Unsafe) field.get(null);

打印顺序不对,影响了代码的正确性

/ * 出队 */ public void dequeue() { while (true) { //如果没有数据 if (length.get() <= 0) { continue; } //获取头节点 Node oldHead = this.head; Node oldNext = oldHead.getNext(); if (unsafe.compareAndSwapObject(this, headOffset, head, oldNext)) { //正确的位置 //System.out.println(Thread.currentThread().getName() + "出队列:" + head.getValue()); length.decrementAndGet(); //错误的位置 System.out.println(Thread.currentThread().getName() + "出队列:" + head.getValue()); break; } } }

一开始我在错误的位置打印,发现入队和出队的顺序不一样,后来换了一个位置试了一下,好了。最后还是分析一下为什么吧。

比如此时此刻 队列里有2个元素A和B。现在2个线程按照下面的顺序执行,其实理论上出队顺序是没有问题的,只不过后面的先打印了,给了一种先出队的错觉。

收获

其实JAVA 无锁队列/栈_meiyongdesan的博客-CSDN博客 这个里面使用AtomicReference实现的,主要想用他的cas;但是我感觉有些绕,所以就自己用unsafe类实现cas。

断断续续写了一天才写了一个demo,其实不亏,至少写出来了。

参考

JAVA 无锁队列/栈_meiyongdesan的博客-CSDN博客

说说Java的Unsafe类 - 简书

关于通过Unsafe.getUnsafe()方法拿Unsafe对象抛出SecurityException异常的原因 - 言午12138 - 博客园

ConcurrentLinkedQueue源码

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/213356.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午6:01
下一篇 2026年3月18日 下午6:01


相关推荐

  • D. 【NOIP2012普及组真题】文化之旅

    D. 【NOIP2012普及组真题】文化之旅题解:–这是一道真水题,说实话,正解不会,因为我们的测试数据所有的文化都不排斥,这就很美丽了……..–华丽丽的Floyd就来了…–注意极大值不要超范围了,是真绝望!代码:#include&lt;iostream&gt;#include&lt;cmath&gt;#include&lt;cstring&gt;#include&lt;cstdio&gt;#inc…

    2022年8月22日
    10
  • vs的安装包_vs2019制作安装包

    vs的安装包_vs2019制作安装包VS安装包注册com组件VS安装包注册com组件1.把你的com组件加入到打包程序。 2.在打包程序中找到该com组件,点击属性。在属性中有Register项,把值选择为vsdrfCOM即可。

    2022年8月22日
    9
  • 白盒测试和黑盒测试的区别

    白盒测试和黑盒测试的区别白盒测试 是指实际运行被测程序 通过程序的源代码进行测试而不使用用户界面 这种类型的测试需要从代码句法发现内部代码在算法 溢出 路径和条件等方面的缺点或者错误 进而加以修正 白盒测试把测试对象看作一个打开的盒子 黑盒测试 又称功能测试 数据驱动测试或基于规格说明的测试 是通过使用整个软件或某种软件功能来严格地测试 而并没有通过检查程序的源代码 或者很清楚地了解该软件的源代码程序具体是

    2025年8月10日
    4
  • 记laravel项目,本地环境PHP7.1,线上PHP版本7.2,报错each函数废弃问题

    记laravel项目,本地环境PHP7.1,线上PHP版本7.2,报错each函数废弃问题

    2022年2月17日
    40
  • java对象类型转换_cdr轮廓转换为对象

    java对象类型转换_cdr轮廓转换为对象要将Java对象或POJO(普通旧Java对象)转换为JSON,我们可以使用JSONObject将对象作为参数的构造函数之一。在下面的示例中,我们将StudentPOJO转换为JSON字符串。Student类必须提供getter方法,JSONObject通过调用这些方法创建JSON字符串。在此代码段中,我们执行以下操作:使用setter方法创建Student对象并设置其属性。 创建JSONObject调用object并将Student对象用作其构造函数的参数。 J.

    2026年1月17日
    6
  • java static关键字的作用是什么_java中的static关键字

    java static关键字的作用是什么_java中的static关键字一、static代表着什么在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,当然也可以修饰代码块。Java把内存分为栈内存和堆内存,其中栈内存用来存放一些基本类型的变量、数组和对象的引用,堆内存主要存放一些对象。在JVM加载一个类的时候,若该类存在static修饰的成员变量…

    2022年7月8日
    19

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号