论文地址:implementing Lock-Free Queue

论文大体讲的意思是:Lock-Base的程序的performance不好,并且a process inside the critical section can delay all operations indenitely;所以基于以上的弊端,他们提出了Non-Blocking的算法,也就是CSW和FAA,当然就是CAS,而CAS也有最难以handler的情况,也就是ABA问题,他们给出了solution,也就是检查引用;他们分别给出了链表场景和数组场景的algorithm,最后是性能分析。

这篇笔记主要为了给Lock-Free提供一些实现方法和思路。

我们先看链表的情况:

双端链表,入队Enqueue方法是tail移动,出队Dequeue是Head移动,下面记录一下伪代码。

Enqueue(x)
q new record
q^:value x
q^:next NULL
repeat
p tail
succ Compare&Swap(p^:next, NULL, q)
if succ = TRUE
Compare&Swap(tail ; p; p^:next)
until succ = TRUE
Compare&Swap(tail ; p; q)
end
Dequeue()
repeat
p head
if p^:next = NULL
error queue empty
until Compare&Swap(head ; p; p^:next)
return p^:next^:value
end

Enqueue的思路是,先设置tail的next指针,如果成功,则把tail指针移动;Dequeue的思路是,如果head的copy p的next不为空,则进行移动,并在成功之后返回之前p的next的值,Java没有指针操作,只有使用unSafe类进行内存操作。(数组实现要比链表实现稳定多了)在跑线程的时候,add会有几率出现Runnable的情况,目前还不知道什么原因,最初的原因是把变量赋值写在for(;;)里,导致它一直不取值,只做循环体,将赋值写在循环体里好了一些,但还是会出现死循环问题。

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
* Created by MacBook on 2019/4/14.
*/
public class MyLockFreeLinkQueue<E> implements MyQueue<E>{ Node<E> head;
Node<E> tail; static Unsafe unsafe; private static final long headOffset;
private static final long tailOffset;
private static final long nextOffset; static{
try{
Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); singleoneInstanceField.setAccessible(true); unsafe = (Unsafe)singleoneInstanceField.get(null); headOffset = unsafe.objectFieldOffset
(MyLockFreeLinkQueue.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(MyLockFreeLinkQueue.class.getDeclaredField("tail"));
nextOffset = unsafe.objectFieldOffset
(MyLockFreeLinkQueue.Node.class.getDeclaredField("next"));
}catch (Exception e){
throw new Error(e);
}
} static class Node<E>{
E data;
Node<E> next; public Node(E data, Node<E> next) {
this.data = data;
this.next = next;
} public E getData() {
return data;
} public void setData(E data) {
this.data = data;
} public Node<E> getNext() {
return next;
} public void setNext(Node<E> next) {
this.next = next;
}
} public MyLockFreeLinkQueue() {
head = tail = new Node<>(null,null);
} /**
*
Enqueue(x)
q new record
q^:value x
q^:next NULL
repeat
p tail
succ Compare&Swap(p^:next, NULL, q)
if succ 6= TRUE
Compare&Swap(tail ; p; p^:next)
until succ = TRUE
Compare&Swap(tail ; p; q)
end
* @param e
* @return
*/
@Override
public boolean add(E e) {
Node<E> q = new Node<>(e,null);
for(;;){
Node<E> p = tail;
if(unsafe.compareAndSwapObject(p,nextOffset,null,q)){
while(unsafe.compareAndSwapObject(this,tailOffset,p,q));
break;
}
}
return true;
} /**
*
Dequeue()
repeat
p head
if p^:next = NULL
error queue empty
until Compare&Swap(head ; p; p^:next)
return p^:next^:value
end
* @return
*/
@Override
public E take() {
for(;;){
Node<E> p = head,next = p.getNext();
if(next == null){
return null;
}else if(unsafe.compareAndSwapObject(this,headOffset,p,next)){
p.setNext(null);// help gc
return next.getData();
}
}
} }

这里我跑了2个生产者线程,2个消费者线程,数据有序的被消费了。

...
pool-1-thread-3 send [62] to queue; total 193
pool-1-thread-3 send [97] to queue; total 194
pool-1-thread-3 send [25] to queue; total 195
pool-1-thread-3 send [17] to queue; total 196
pool-1-thread-3 send [50] to queue; total 197
pool-1-thread-3 send [72] to queue; total 198
pool-1-thread-3 send [46] to queue; total 199
pool-1-thread-3 send [83] to queue; total 200
pool-1-thread-4 consumer [62],count 2n+1 result :125; total 193
pool-1-thread-4 consumer [97],count 2n+1 result :195; total 194
pool-1-thread-4 consumer [25],count 2n+1 result :51; total 195
pool-1-thread-4 consumer [17],count 2n+1 result :35; total 196
pool-1-thread-4 consumer [50],count 2n+1 result :101; total 197
pool-1-thread-4 consumer [72],count 2n+1 result :145; total 198
pool-1-thread-4 consumer [46],count 2n+1 result :93; total 199
pool-1-thread-4 consumer [83],count 2n+1 result :167; total 200

接下来,我实践了环形数组的实现,基于我之前实现的BlockingQueue,这个Lock-Free Queue会变得比较简单。

import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by MacBook on 2019/4/13.
*/
public class MyLockFreeQueue<E> implements MyQueue<E>{
private Object[] data;
private AtomicInteger takeIndex;
private AtomicInteger putIndex;
private AtomicInteger size;
private static final int DEFAULT_CAPACITY = 10; public MyLockFreeQueue (){
this(DEFAULT_CAPACITY);
}
public MyLockFreeQueue(int initCapacity){
if(initCapacity < 0){
throw new IllegalStateException("initCapacity must not be negative");
}
data = new Object[initCapacity];
takeIndex = new AtomicInteger(0);
putIndex = new AtomicInteger(0);
size = new AtomicInteger(0);
} public boolean add(E e){
if(e == null){
throw new NullPointerException("the element you put can't be null");
}
for(int index = putIndex.get();;){
if(size.get() == data.length){
return false;
}
int expect = (index == data.length - 1)?0:(index+1);
if(putIndex.compareAndSet(index,expect)){
data[index] = e;
size.incrementAndGet();
return true;
}
}
}
public E take(){
for(int index = takeIndex.get();;){
if(size.get() == 0){
return null;
}
int expect = (index == data.length - 1)?0:(index+1);
E e = (E)data[index];
if(takeIndex.compareAndSet(index,expect)){
size.decrementAndGet();
return e;
}
}
}
}

思路就是,使用两个标记入队和出队的Atom Integer对象,在成功申请当前格子之后,给当前格子赋值,使用size来判断是否EMPTY和FULL。这里依然有一点缺陷,就是index和size不同步的问题,不过我也是跑了2+2线程,也是有序消费了。

...pool-1-thread-3 send [81] to queue; total 188
pool-1-thread-2 consumer [81],count 2n+1 result :163; total 188
pool-1-thread-3 send [1] to queue; total 189
pool-1-thread-2 consumer [1],count 2n+1 result :3; total 189
pool-1-thread-2 consumer [19],count 2n+1 result :39; total 190
pool-1-thread-3 send [19] to queue; total 190
pool-1-thread-3 send [61] to queue; total 191
pool-1-thread-2 consumer [61],count 2n+1 result :123; total 191
pool-1-thread-3 send [16] to queue; total 192
pool-1-thread-2 consumer [16],count 2n+1 result :33; total 192
pool-1-thread-3 send [74] to queue; total 193
pool-1-thread-2 consumer [74],count 2n+1 result :149; total 193
pool-1-thread-3 send [38] to queue; total 194
pool-1-thread-2 consumer [38],count 2n+1 result :77; total 194
pool-1-thread-3 send [32] to queue; total 195
pool-1-thread-2 consumer [32],count 2n+1 result :65; total 195
pool-1-thread-3 send [9] to queue; total 196
pool-1-thread-2 consumer [9],count 2n+1 result :19; total 196
pool-1-thread-3 send [77] to queue; total 197
pool-1-thread-2 consumer [77],count 2n+1 result :155; total 197
pool-1-thread-3 send [69] to queue; total 198
pool-1-thread-2 consumer [69],count 2n+1 result :139; total 198
pool-1-thread-3 send [52] to queue; total 199
pool-1-thread-2 consumer [52],count 2n+1 result :105; total 199
pool-1-thread-3 send [81] to queue; total 200
pool-1-thread-2 consumer [81],count 2n+1 result :163; total 200
        ExecutorService executor = Executors.newFixedThreadPool(6);
MyLockFreeQueue<Integer> queue = new MyLockFreeQueue();
Worker<Integer> pro = new Provider(queue);
Worker<Integer> con = new Consumer(queue); executor.submit(pro);
executor.submit(con);
executor.submit(pro);
executor.submit(con);
executor.submit(pro);
executor.submit(con); executor.shutdown();

读Lock-Free论文实践的更多相关文章

  1. 读Lua游戏开发实践指南

    11月11日开读,到今天正好一个月. 起因是被裁员之后,发现很多公司都在使用lua编写cocos2d-x游戏,原因是上手快,技术人员比较便宜. 如果引擎封装比较好,几乎在lua里写写基本逻辑就行了,不 ...

  2. 读Java并发编程实践中,向已有线程安全类添加功能--客户端加锁实现示例

    在Java并发编程实践中4.4中提到向客户端加锁的方法.此为验证示例,写的不好,但可以看出结果来. package com.blackbread.test; import java.util.Arra ...

  3. 读unp并动手实践

    经过三个月的学习,我发现进度比较慢.照这个进度下去,平均一周花费5-6小时,还不知道读完全书需要多久. 现在做个计划,全书除开简介部分分为 基础 和 高级 套接字编程两部分,其中 基础可以分为 TCP ...

  4. 跟我读CVPR 2022论文:基于场景文字知识挖掘的细粒度图像识别算法

    摘要:本文通过场景文字从人类知识库(Wikipedia)中挖掘其背后丰富的上下文语义信息,并结合视觉信息来共同推理图像内容. 本文分享自华为云社区<[CVPR 2022] 基于场景文字知识挖掘的 ...

  5. Deep Learning 28:读论文“Multi Column Deep Neural Network for Traffic Sign Classification”-------MCDNN 简单理解

    读这篇论文“ Multi Column Deep Neural Network for Traffic Sign Classification”是为了更加理解,论文“Multi-column Deep ...

  6. PayPal高级工程总监:读完这100篇论文 就能成大数据高手(附论文下载)

    100 open source Big Data architecture papers for data professionals. 读完这100篇论文 就能成大数据高手 作者 白宁超 2016年 ...

  7. mysql系列:加深对脏读、脏写、可重复读、幻读的理解

    关于相关术语的专业解释,请自行百度了解,本文皆本人自己结合参考书和自己的理解所做的阐述,如有不严谨之处,还请多多指教. 事务有四种基本特性,叫ACID,它们分别是: Atomicity-原子性,Con ...

  8. (5.15)mysql高可用系列——MHA实践

    关键词:MHA,mysql mha [1]需求 采用mysql技术,实现MHA高可用主从环境,预计未来数据量几百G MHA概念参考:MYSQL高可用技术概述 [2]环境技术架构 [2.1]MHA简介 ...

  9. 5213 Exp3 免杀原理与实践

    5213 Exp3 免杀原理与实践 任务一:正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己利用shellcode编程等免杀工具或技巧 使用msf编码器 ...

随机推荐

  1. [Jmeter]通过批处理调用java,java从CSV动态读取登录的用户名和密码,并将其作为参数组合成字符串,写入外部.bat文件,然后通过Java执行这个外部批处理文件

    问题1:怎样通过批处理调用java代码? 问题2:怎样通过java从CSV文件获取到用户名和密码存入变量? 问题3:怎样将获取到的用户名和密码组合成字符串,写入外部批处理文件? 问题4:怎样在批处理文 ...

  2. iis 应用程序预热

    <applicationPools> <add name="appname" managedRuntimeVersion="v4.0" sta ...

  3. JavaScript的异步运行机制

    ----异步运行机制如下: 1.左右同步任务都在主线程上执行,形成一个执行栈 2.主线程值外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件 3.一旦执行栈中的所有同步任务执 ...

  4. swiper3d横向滚动多张炫酷切换banner

    最近有了个新需求,swiper3d横向滚动多张炫酷切换banner要和elementUI里边走马灯的卡片化card 类似,但是还需要h5手机触摸滚动啊啊啊啊,昨天折腾了半个早上总算完成,今天乖乖跑来m ...

  5. Product of integers

    https://github.com/Premiumlab/Python-for-Algorithms--Data-Structures--and-Interviews/blob/master/Moc ...

  6. 2018.06.29 NOIP模拟 繁星(前缀和)

    繁星 [问题描述] 要过六一了,大川正在绞尽脑汁想送给小伙伴什么礼物呢.突然想起以前拍过一张夜空中的繁星的照片,这张照片已经被处理成黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑.像素(x,y ...

  7. 处理jQuery选择器中的特殊符号,如(、#等

    前几天解决一个外网问题,客服反馈页面数据加载不出来,首先看一下服务端日志也没报错异常,自己测试了一下,在chrome的Console发现有js报错,原来是js报错导致的数据加载不出来. 调试了一番,发 ...

  8. Python网络编程总结

    ----learn from luffycity---- 1. 什么是C/S架构? C指的是client(客户端软件),S指的是Server(服务端软件),C/S架构就是基于网络实现客户端与服务端通信 ...

  9. where /group by/ having/ order by/

    1.order by 是 按字段 进行排序.. 字段后面可跟 desc 降序..asc 升序..默认为升序2.group by 是进行分组 查询3.having 和 where 都属于 条件过滤 区别 ...

  10. 事务不起作用 Closing non transactional SqlSession

    In proxy mode (which is the default), only external method calls coming in through the proxy are int ...