读Lock-Free论文实践

论文地址: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 6= 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类进行内存操作。

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;

    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"));

        }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(this,tailOffset,p,q)){
                p.setNext(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();

原文地址:https://www.cnblogs.com/chentingk/p/10705141.html

时间: 2024-11-09 10:40:45

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

01读《软件构架实践》后感

寒假生活 读<软件构架实践>1-3章后感 看到书名,心里便对"软件构架"产生了一个模糊的轮廓,根据现有的经验,我的理解是:既然被称作是一个构架,那应该是一个系统的骨干之处,是撑起整个系统的复杂结构.可能是有的人早时设计一个系统的构架,后来被人们广泛使用,最终演变成为适用于多个系统的多样式构架,像我们学过的设计模式,被人熟练运用丰富后成为开发的经验. 在阅读之后,我找到了构架的确切定义:某个软件或计算机系统的软件构架是该系统的一个或多个结构,他们由软件元素(特征损失模型MOD

【转】研究生如何读文献 写论文 发文章 毕业论文

研究生论文写作步骤 1. 先看综述,后看论著.看综述搞清概念,看论著掌握方法.2. 早动手在师兄师姐离开之前学会关键技术.3. 多数文章看摘要,少数文章看全文.掌握了一点查全文的技巧,往往会以搞到全文为乐,以至于没有时间看文章的内容,更不屑于看摘要.真正有用的全文并不多,过分追求全文是浪费,不可走极端.当然只看摘要也是不对的.4. 集中时间看文献,看过总会遗忘.看文献的时间越分散,浪费时间越多.集中时间看更容易联系起来,形成整体印象.5. 做好记录和标记复印或打印的文献,直接用笔标记或批注.pd

如何读一篇论文

研究人员每年需要阅读大量的paper,因此,高效的阅读paper是至关重要的. 我们应该使用三步法阅读paper: 第一步: 第一步应该用鸟瞰一般的视野对paper进行快速浏览,可以根据自己想获取的信息作更多的选择性浏览.这一步大约花费5-10分钟的时间且由以下步骤组成: 1.认真阅读标题,摘要和介绍. 2.阅读章节和子章节的标题 3.浏览数学内容(如果有的话)来确定基本的理论基础 4.读取实验结果 5.浏览一下references,在心里把你已经读过的内容勾出来 完成第一步后,你需要能够回答一

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

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

管理是什么?——读《管理的实践》有感

花了4个月的时间,总算完完整整的读完了<管理的实践>一书.书中诸多观点在现在看来,依旧很新颖,不亏是管理类书籍的开山之作. 这本书二战刚结束就开始创作,而今很多企业都做不到.与其说是实践,倒不如说是管理的思想.这本书传道受业解惑,但又不可避免的造成"知易行难"的困境. 书中阐明了企业是什么.企业目标是什么.管理 的本质是什么.企业需要哪一种管理的结构.如何做决策等几个看似简单又难以回答的问题. 本书以管理企业.管理管理者.管理员工和工作的顺序开展,完整的阐述了作者对管理的思

SCI论文从入门到精通

第一部分 经验谈 一.先想先写最后做 做研究之前,必须想清楚:结果能不能发表?发表在哪里? 先把文章大框写好,空出数据,等做完实验填完空就可以发了:正所谓心中有沟壑! 在未搞清“写什么.发哪里.自己研究与同类研究有何出色之处”之前,就不要动手做! 继续去看文献,去想:想不清楚就做还不如不做! 要想这样做,就得先看文献!要知道如何把文章架起来.要知道别人是如何讨论的.要知道自己的数据是不是说明了与别人不同的东东或别人没有做过……这个过程就是阅读文献及思考的过程,这些搞清楚了,写就简单了! 要是先做

论文战略

最近论文实验进行的非常的不顺利,一方面是实验聚类的效果十分的差,另一方面是做大数据的实验对计算机的性能要求很高,非常的耗费内存,每当矩阵的维度大于3000的时候,整个计算机就像中了魔法似的,像蜗牛一样慢.这样用小数据做实验效果非常的差,用大数据做实验计算机又跑不动,搞的自己很愚昧,不知道是自己数据集的问题,还是聚类算法的问题. 通过读论文,做实验,让自己学会了坚忍,深刻地理解了用机器学习处理大规模数据所带来的挑战,以及明白了无论做什么事情都要上心. 普卢塔克说,萨特斯为了告诉他的士兵坚忍和智慧比

《Real-Time Compressive Tracking》论文理解

     这是Kaihua Zhang发表在ECCV2012的paper,paper的主题思想是利用满足压缩感知(compressive sensing)的RIP(restricted isometry property)条件的随机测量矩阵(random measurement matrix)对多尺度(multiple scale)的图像特征(features)进行降维,然后通过朴素贝叶斯分类器(naive Bayes classifier)对特征进行分类预测目标位置.   首先介绍下paper

读完这100篇论文 就能成大数据高手(附论文下载)

100 open source Big Data architecture papers for data professionals. 读完这100篇论文 就能成大数据高手 作者 白宁超 2016年4月16日13:38:49 摘要:本文基于PayPal高级工程总监Anil Madan写的大数据文章,其中涵盖100篇大数据的论文,涵盖大数据技术栈(数据存储层.键值存储.面向列的存储.流式.交互式.实时系统.工具.库等),全部读懂你将会是大数据的顶级高手.作者通过引用Anil Madan原文和CS