论文地址: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. 2. Get the codes from GIT

    Clone the code from git.

  2. Can't use Subversion command line client: svn. Errors found while svn working copies detection.

    idea 报错: Can't use Subversion command line client: svn. Errors found while svn working copies detect ...

  3. idea中实体类序列化后生成序列化版本ID的方法

    为什么要添加序列化版本ID了(serialVersionUID)? 通过判断实体类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVers ...

  4. 第四章 代词(Les pronoms )

    ★人称代词 .主语人称代词 第一人称和第二人称属纯人称代词,只能代人不能代物;第三人称可代人,亦可代物.如: La Terre est ronde. Elle tourne autour du Sol ...

  5. Part 6 - Class-Based Views(21-26)

    https://github.com/sibtc/django-beginners-guide/tree/v0.6-lw urlpatterns = [ views.PostUpdateView.as ...

  6. HDU 1716 排列2 (格式问题+排列)

    题意:. 析:我们完全可以STL里面的函数next_permutation(),然后方便,又简单,这个题坑就是在格式上. 行末不能有空格,结尾不能有空行,不大好控制,必须控制好第一次数. 这个题本应该 ...

  7. python关键的语法

    python关键的语法 1.标准类型分类

  8. hdu 5685 Problem A (逆元)

    题目 题意:H(s)=∏i≤len(s)i=1(Si−28) (mod 9973),求一个字符串 子串(a 位到 b 位的)的哈希值.这个公式便是求字符串哈希值的公式,(字符的哈希值 = 字符的ASC ...

  9. java web基础之mvc模式设计(一)--使用httpservlet实现mvc分层设计,DAO层使用的是dbutils实现与数据库的链接

    一:1.最终的实现效果图: 2.案例的目录结构: 3.案例中使用到的jar包: 二:案例的链接数据库的层次结构关系:数据库是:mysql ,数据库名字:dsm,表格名字:customers 数据库表格 ...

  10. delphi_xe开发ios环境的安装与设置

     http://wenku.baidu.com/link?url=NE3xJOZiLppdxCbXJX3W0vyLHv6uA_U8uamjx9NJIIcxnfuC2P9eWx3d6Xwco-ugS8G ...