非阻塞算法(Lock-Free)的实现

上篇文章我们讲到了使用锁会带来的各种缺点,本文将会讲解如何使用非阻塞算法。非阻塞算法一般会使用CAS来协调线程的操作。

虽然非阻塞算法有诸多优点,但是在实现上要比基于锁的算法更加繁琐和负责。

本文将会介绍两个是用非阻塞算法实现的数据结构。

非阻塞的栈

我们先使用CAS来构建几个非阻塞的栈。栈是最简单的链式结构,其本质是一个链表,而链表的根节点就是栈顶。

我们先构建Node数据结构:

public class Node<E> {
public final E item;
public Node<E> next; public Node(E item){
this.item=item;
}
}

这个Node保存了内存item和它的下一个节点next。

然后我们构建非阻塞的栈,在该栈中我们需要实现pop和push方法,我们使用一个Atomic类来保存top节点的引用,在pop和push之前调用compareAndSet命令来保证命令的原子性。同时,我们需要不断的循环,以保证在线程冲突的时候能够重试更新。

public class ConcurrentStack<E> {

    AtomicReference<Node<E>> top= new AtomicReference<>();

    public void push(E item){
Node<E> newNode= new Node<>(item);
Node<E> oldNode;
do{
oldNode=top.get();
newNode.next= oldNode;
}while(!top.compareAndSet(oldNode, newNode));
} public E pop(){
Node<E> oldNode;
Node<E> newNode;
do {
oldNode = top.get();
if(oldNode == null){
return null;
}
newNode=oldNode.next;
}while(!top.compareAndSet(oldNode, newNode));
return oldNode.item;
} }

非阻塞的链表

构建链表要比构建栈复杂。因为我们要维持头尾两个指针。以put方法来说,我们需要执行两步操作:1. 在尾部插入新的节点。2.将尾部指针指向最新的节点。

我们使用CAS最多只能保证其中的一步是原子执行。那么对于1和2的组合步骤该怎么处理呢?

我们再仔细考虑考虑,其实1和2并不一定要在同一个线程中执行,其他线程在检测到有线程插入了节点,但是没有将tail指向最后的节点时,完全帮忙完成这个操作。

我们看下具体的代码实现:

public class LinkedNode<E> {
public final E item;
public final AtomicReference<LinkedNode<E>> next; public LinkedNode(E item, LinkedNode<E> next){
this.item=item;
this.next=new AtomicReference<>(next);
}
}

先构建一个LinkedNode类。

public class LinkedQueue<E> {
private final LinkedNode<E> nullNode= new LinkedNode<>(null, null);
private final AtomicReference<LinkedNode<E>> head= new AtomicReference<>(nullNode);
private final AtomicReference<LinkedNode<E>> tail= new AtomicReference<>(nullNode); public boolean put(E item){
LinkedNode<E> newNode = new LinkedNode<>(item, null);
while (true){
LinkedNode<E> currentTail= tail.get();
LinkedNode<E> tailNext= currentTail.next.get();
if(currentTail == tail.get()){
if (tailNext != null) {
//有其他的线程已经插入了一个节点,但是还没有将tail指向最新的节点
tail.compareAndSet(currentTail, tailNext);
}else{
//没有其他的线程插入节点,那么做两件事情:1. 插入新节点,2.将tail指向最新的节点
if(currentTail.next.compareAndSet(null, newNode)){
tail.compareAndSet(currentTail, newNode);
}
}
}
}
}
}

本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/nonblock

更多内容请访问 http://www.flydean.com/java-lock-free/

非阻塞算法(Lock-Free)的实现的更多相关文章

  1. java并发之非阻塞算法介绍

    在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在算法中如果一个线程的挂起没有导致其它的线程挂起,我们就说这个算法是非阻塞的. 为了更好的理解阻塞算 ...

  2. 29、Java并发性和多线程-非阻塞算法

    以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在 ...

  3. Java锁与非阻塞算法的性能比较与分析+原子变量类的应用

    15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...

  4. Java 理论与实践: 非阻塞算法简介——看吧,没有锁定!(转载)

    简介: Java™ 5.0 第一次让使用 Java 语言开发非阻塞算法成为可能,java.util.concurrent 包充分地利用了这个功能.非阻塞算法属于并发算法,它们可以安全地派生它们的线程, ...

  5. Java 理论与实践: 非阻塞算法简介--转载

    在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是synchronized 关键字(也称为内在锁),它强制实行互斥,确保 ...

  6. 《Java并发编程实战》笔记-非阻塞算法

    如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...

  7. 基于CAS操作的非阻塞算法

    非阻塞算法(non-blocking algorithms)定义        所谓非阻塞算法是相对于锁机制而言的,是指:一个线程的失败或挂起不应该引起另一个线程的失败或挂起的一种算法.一般是利用硬件 ...

  8. 《java并发编程实战》读书笔记12--原子变量,非阻塞算法,CAS

    第15章 原子变量与非阻塞同步机制 近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令(例如比较并交换指令)代替锁老确保数据在并发访问中的一致性. 15.1 锁的劣势 ...

  9. Java 理论与实践-非阻塞算法简介

    在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是 synchronized 关键字(也称为内在锁),它强制实行互斥,确 ...

随机推荐

  1. 如何使用Rancher在OpenStack上创建K8S集群

    不可否认的是,OpenStack仍然是可行的云操作系统,并且被全世界许多互联服务提供商使用.而Rancher是业界最为广泛使用的Kubernetes管理平台,通过简洁直观的GUI集中管理企业IT中的多 ...

  2. 使用git上传代码到GitHub

    1.安装git git在Windows上安装很简单,在官网下载git的安装包后打开,然后一路next就好.安装完git之后,在文件夹中右击鼠标,出现Git Bash Here就表示安装完成了. 选择G ...

  3. PTA数据结构与算法题目集(中文) 7-28

    PTA数据结构与算法题目集(中文)  7-28 7-28 搜索树判断 (25 分)   对于二叉搜索树,我们规定任一结点的左子树仅包含严格小于该结点的键值,而其右子树包含大于或等于该结点的键值.如果我 ...

  4. Linux基础:Day04

    进程管理1.操作系统基础 调用:kernel通过给应用程序提供system call方式来提供硬件资源:  注意:这个应用程序也包括库文件:  库文件是运行在ring 0上的一段程序代码,不对客户直接 ...

  5. 【Linux】nginx服务配置

    一. 部署LNMP环境 准备工作 Linux系统准备 设置IP 关闭防火墙 yum源配置 安装: 传输软件包 1. tar -zxvf lnmp1.2-full.tar.gz cd lnmp1.2-f ...

  6. 【高并发】你知道吗?大家都在使用Redisson实现分布式锁了!!

    写在前面 忘记之前在哪个群里有朋友在问:有出分布式锁的文章吗-@冰河?我的回答是:这周会有,也是[高并发]专题的.想了想,还是先发一个如何使用Redisson实现分布式锁的文章吧?为啥?因为使用Red ...

  7. Linux Shell编程,双括号运算符(())

    双括号运算符是shell非常强大的扩展. 这里简要介绍两种使用方式: 1.条件判断 跟在if.while.until,for等需要逻辑条件的命令后,进行逻辑判断 if(( expr));then … ...

  8. 初始WebApi(1)

    如果你要问我WebApi是干嘛,我只能说它是的给数据.哈哈哈哈哈,这几天也才刚刚了解了解关于WebApi的知识,今天就来谈谈吧. 1.创建WebApi项目 第一步:选择ASP.NET Web应用程序 ...

  9. python3(三十一)metaclass

    """ """ __author__ = 'shaozhiqi' # 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而 ...

  10. python3(六) for while

    # Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来 names = ['Michael', 'Bob', 'Tracy'] for name in ...