非阻塞算法(Lock-Free)的实现
非阻塞算法(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)的实现的更多相关文章
- java并发之非阻塞算法介绍
在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在算法中如果一个线程的挂起没有导致其它的线程挂起,我们就说这个算法是非阻塞的. 为了更好的理解阻塞算 ...
- 29、Java并发性和多线程-非阻塞算法
以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在 ...
- Java锁与非阻塞算法的性能比较与分析+原子变量类的应用
15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...
- Java 理论与实践: 非阻塞算法简介——看吧,没有锁定!(转载)
简介: Java™ 5.0 第一次让使用 Java 语言开发非阻塞算法成为可能,java.util.concurrent 包充分地利用了这个功能.非阻塞算法属于并发算法,它们可以安全地派生它们的线程, ...
- Java 理论与实践: 非阻塞算法简介--转载
在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是synchronized 关键字(也称为内在锁),它强制实行互斥,确保 ...
- 《Java并发编程实战》笔记-非阻塞算法
如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...
- 基于CAS操作的非阻塞算法
非阻塞算法(non-blocking algorithms)定义 所谓非阻塞算法是相对于锁机制而言的,是指:一个线程的失败或挂起不应该引起另一个线程的失败或挂起的一种算法.一般是利用硬件 ...
- 《java并发编程实战》读书笔记12--原子变量,非阻塞算法,CAS
第15章 原子变量与非阻塞同步机制 近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令(例如比较并交换指令)代替锁老确保数据在并发访问中的一致性. 15.1 锁的劣势 ...
- Java 理论与实践-非阻塞算法简介
在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是 synchronized 关键字(也称为内在锁),它强制实行互斥,确 ...
随机推荐
- 使用 Visual Studio 开发、测试和部署 Azure Functions(二)测试,部署
1,引言 上一篇介绍了使用使用 Visual Studio 开发 "Azure Functions" 函数,此篇介绍 “Azure Functions” 的测试以及直接从 Vist ...
- 苦涩的技术我该怎么学?Akka 实战
上次我们在“懵 B”的状态下,聊了聊 Actor 模型的理论知识.稍微再补充两句,如上图所示在 Actor 模型系统中,主要有互不依赖的 Actor 组成(图中圆圈),Actor 之间的通信是通过消息 ...
- Azure SDK for Python Url
Azure SDK for Python URL 明细表 AZURE_PUBLIC_CLOUD = Cloud( 'AzureCloud', endpoints=CloudEndpoints( man ...
- react axios 跨域访问一个或多个域名
1.react + axios 跨域访问一个域名 配置非常简单,只需要在当前的 package.json 文件里面配置: "proxy":"http://iot-demo ...
- PTA数据结构与算法题目集(中文) 7-20
PTA数据结构与算法题目集(中文) 7-20 7-20 表达式转换 (25 分) 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个 ...
- Disruptor 基础篇
Disruptor 基本概念 RingBuffer结构 Sequencer (生产.消费协调者) EventFactory & EventTranslator SequenceBarrier ...
- Scrapy-02-item管道、shell、选择器
Scrapy-02 item管道: scrapy提供了item对象来对爬取的数据进行保存,它的使用方法和字典类似,不过,相比字典,item多了额外的保护机制,可以避免拼写错误和定义字段错误. 创建的i ...
- docker中的dockerfile
什么是dockerfile? Dockerfile是一个包含用于组合映像的命令的文本文档.可以使用在命令行中调用任何命令. Docker通过读取Dockerfile中的指令自动生成映像. docker ...
- vscode连接云服务,搭建Python远程开发
配置Python远程开发环境前提 配置步骤 1.windows 10 开发机配置 win10 1809后支持ssh ssh-keygen -t rsa -b 4096 #会显示生成到的目录C:\Use ...
- L6循环神经网络
循环神经网络 本节介绍循环神经网络,下图展示了如何基于循环神经网络实现语言模型.我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符.循环神经网络引入一个隐藏变量HHH,用HtH_{t}H ...