如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法。如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法。如果在算法中仅将CAS用于协调线程之间的操作,并且能正确地实现,那么它既是一种无阻塞算法,又是一种无锁算法。

创建非阻塞算法的关键在于,找出如何将原子修改的范围缩小到单个变量上,同时还要维护数据的一致性。

非阻塞算法的所有特性:某项工作的完成具有不确定性,必须重新执行。

复杂数据结构的非阻塞算法,如非阻塞链接队列,因为它必须支持对头节点和尾节点的快速访问,因此它需要单独维护头指针和尾指针。插入新元素时需要采用两个原子操作更新当前最后元素指针和尾指针,这会使队列处于不一致状态。我们需要使用一些技巧,第一是,即使在一个包含多个步骤的更新操作中,也要确保数据结构总是处于一致状态。第二是,如果当B到达时发现A正在修改数据结构,那么在数据结构中就应该有足够的信息,使得B能完成A的更新操作。

Michael-Scott(Michael and Scott,1996)非阻塞算法中的插入算法

package net.jcip.examples;

import java.util.concurrent.atomic.*;

import net.jcip.annotations.*;

/**
* LinkedQueue
* <p/>
* Insertion in the Michael-Scott nonblocking queue algorithm
*
* @author Brian Goetz and Tim Peierls
*/
@ThreadSafe
public class LinkedQueue <E> { private static class Node <E> {
final E item;
final AtomicReference<LinkedQueue.Node<E>> next; public Node(E item, LinkedQueue.Node<E> next) {
this.item = item;
this.next = new AtomicReference<LinkedQueue.Node<E>>(next);
}
} private final LinkedQueue.Node<E> dummy = new LinkedQueue.Node<E>(null, null);
private final AtomicReference<LinkedQueue.Node<E>> head
= new AtomicReference<LinkedQueue.Node<E>>(dummy);
private final AtomicReference<LinkedQueue.Node<E>> tail
= new AtomicReference<LinkedQueue.Node<E>>(dummy); public boolean put(E item) {
LinkedQueue.Node<E> newNode = new LinkedQueue.Node<E>(item, null);
while (true) {
LinkedQueue.Node<E> curTail = tail.get();
LinkedQueue.Node<E> tailNext = curTail.next.get();
if (curTail == tail.get()) {
if (tailNext != null) {
// Queue in intermediate state, advance tail
tail.compareAndSet(curTail, tailNext);
} else {
// In quiescent state, try inserting new node
if (curTail.next.compareAndSet(null, newNode)) {
// Insertion succeeded, try advancing tail
tail.compareAndSet(curTail, newNode);
return true;
}
}
}
}
}
}

ABA问题,在某些算法中,如果V的值首先由A变成B,再由B变成A,那么仍然被认为是发生了变化,并需要重新执行算法中的某些步骤。Java类库提供AtomicStampedReferencet和AtomicMarkableReference处理ABA问题。AtomicStampedReferencet将更新一个“对象-引用”二元组,通过在引用上加上“版本号”,从而避免ABA问题。AtomicMarkableReference将更新一个“对象引用-布尔值”二元组,在某些算法中将通过这种二元组使节点保存在链表中同时又将其标记为“已删除的节点”。

《Java并发编程实战》笔记-非阻塞算法的更多相关文章

  1. Java并发编程实战.笔记十一(非阻塞同步机制)

    关于非阻塞算法CAS. 比较并交换CAS:CAS包含了3个操作数---需要读写的内存位置V,进行比较的值A和拟写入的新值B.当且仅当V的值等于A时,CAS才会通过原子的方式用新值B来更新V的值,否则不 ...

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

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

  3. 多线程-java并发编程实战笔记

    线程安全性 编写线程安全的代码实质上就是管理对状态的访问,而且通常都是共享的,可变的状态. 一个对象的状态就是他的数据,存储在状态变量中,比如实例域或静态域.所谓共享是指一个对象可以被多个线程访问:所 ...

  4. Java并发编程实战笔记—— 并发编程1

    1.如何创建并运行java线程 创建一个线程可以继承java的Thread类,或者实现Runnabe接口. public class thread { static class MyThread1 e ...

  5. Java并发编程实战笔记

    如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误.有三种方式可以修复这个问题: i.不在线程之间共享该状态变量 ii.将状态变量修改为不可变的变量 iii.在访问状态变 ...

  6. java并发编程实战笔记---(第五章)基础构建模块

    . 5.1同步容器类 1.同步容器类的问题 复合操作,加容器内置锁 2.迭代器与concurrentModificationException 迭代容器用iterator, 迭代过程中,如果有其他线程 ...

  7. java并发编程实战笔记---(第三章)对象的共享

    3.1 可见性 synchronized 不仅实现了原子性操作或者确定了临界区,而且确保内存可见性. *****必须在同步中才能保证:当一个线程修改了对象状态之后,另一个线程可以看到发生的状态变化. ...

  8. java并发编程实战笔记---(第四章)对象的组合

    4.1设计线程安全的类 包含三个基本要素: 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 2.简历对象状态的并发访问管理策略 对象的状态: 域 基本类型所有域, 引用类型包括被引用 ...

  9. Java并发编程实战笔记—— 并发编程4

    1.同步容器类 同步容器类都是线程安全的,但在某些情况下可能需要额外的客户端加锁保护复合操作. 容器上常见的复合操作包括但不限于:迭代(反复访问数据,直到遍历完容器中所有的元素为止).跳转(根据指定顺 ...

  10. Java并发编程实战笔记—— 并发编程2

    1.ThreadLocal Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作.因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有一个指向同一个ThreadL ...

随机推荐

  1. PTA——支票面额

    PTA 7-38 支票面额 #include<stdio.h> int main() { int n,f,y; ; scanf("%d",&n); ; flag ...

  2. c++——数据结构

    1.写一个函数PrintN,使得传入一个N,打印从1到N的全部整数 #include<stdio.h> //循环实现 void PrintN(int N){ int i; ;i<=N ...

  3. mysql条件查询-排除null ---oracle、mysql 区分总结

    ' AND IFNULL(c.`COURSE_NO`,'-1') != '-1' ;

  4. 访问 iframe 内部控件方法

    方法虽然简单,但是经常忘记,网上一查,很多方法兼容性都有问题,这段代码至少兼容IE和Chrome setInterval(function(){ document.getElementById('ma ...

  5. gtk_init()

    #include<stdio.h> #if 0int main(int argc, char *argv[]){ char ***p = &argv; //传参退化成二级指针,对二 ...

  6. 简单 babel plugin 开发-使用lerna 工具

    babel在现在的web 应用开发上具有很重要的作用,帮助我们做了好多事情,同时又有 比较多的babel plugin 可以解决我们实际开发上的问题. 以下只是学习下如果编写一个简单的babel pl ...

  7. lapis 项目添加prometheus 监控

      lapis 是基于openresty 扩展的,所以直接将支持prometheus的模块构建进openresty 就可以了 我使用的是nginx-module-vts 模块 环境准备 我已经构建好了 ...

  8. What happens to our code? JavaScript 代码是怎样执行的

    1. 我们的代码第一步会被parser 语法分析程序分析. 如果没有报错之后 2. 生产SyntaxTree, 我们的代码会转换成machine code 3. 最终 我们的代码会被运行出来. 下面的 ...

  9. Truthy and Falsy Values and Equality Operators

    最近在学习JS.  有一些概念还希望跟大家分享出来 Truthy and Falsy Values Falsy Values: undefined, null, 0, '', NaN(not a nu ...

  10. LoadRunner内部介绍以及常见问题

    Tools Recording Options 接下来我们挨个看一下里面的东东以及区别 General(通常的) Script,不用修改,是脚本的语言,我们一般都是基于 C 语言的 Protocols ...