《java并发编程实战》读书笔记12--原子变量,非阻塞算法,CAS
第15章 原子变量与非阻塞同步机制
近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令(例如比较并交换指令)代替锁老确保数据在并发访问中的一致性。
15.1 锁的劣势
这个不多说了,详细见p262
15.2 硬件对并发的支持
独占锁是一项悲观的技术,它假设最坏的情况。对于细粒度的操作,还有一种乐观的方法,通过这种方法可以在不发生干扰的情况下完成更新操作。这种方法需要借助冲突检查机制来判断在更新过程中是否存在来自其他线程的干扰,如果存在,这个操作将失败,并且可以重试(听起来很像数据库中的乐观锁啊)。
在针对多处理器操作而设计的处理器中提供了一些特殊指令,用于管理对共享数据的并发访问。在早期的处理器中支持原子的测试并设置(Test-and-Set),获取并递增(Fetch-and-Increment)以及交换(Swap)等指令,这些指令足以实现各种互斥体,而这些互斥体又可以实现一些更复杂的并发对象。
15.2.1 比较并交换
在大多数处理器架构中采用的方法是实现一个比较并交换(CAS)指令。


当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都将失败。然而,失败的线程并不会挂起(这与获取锁的情况不同:当获取锁失败时,线程将被挂起),而是被告知在这次竞争中失败,并可以再次尝试。

15.2.2 非阻塞的计数器
程序15-2中的CaseCounter使用了CAS实现了一个线程安全的计数器。

CaseCounter不会阻塞,但如果其他线程同时更新计数器,那么会多次执行重试操作。

15.2.3 JVM对CAS的支持
在java5.0之前,如果不编写明确的代码,那么就无法执行CAS。在java5.0中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS操作(好像就是那些原子变量),并且JVM把它们编译为底层硬件提供的最有效方法。在支持CAS的平台上,运行时把他们编译为相应的机器指令。在最坏情况下,如果不支持CAS指令,那么JVM将使用自旋锁。
15.3 原子变量类
15.3.1 原子变量是一种“更好的volatile”

从这节的描述来看,原子变量利用的是硬件对并发的支持,即CAS
15.3.2 性能比较:锁与原子变量
构造一个测试基准,其中将比较为随机数字生成器(PRNG)的几种不同的实现。在PRNG中,在生成下一个随机数字时需要用到上一个数字,所以在PRNG中必须记录前一个数值并将其作为状态的一部分。程序15-4和15-5给出了线程安全的PRNG的两种实现,一种使用ReentrantLock,另一种使用AtomicInteger。









15.4 非阻塞算法
如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败或挂起,那么这种算法就被称为非阻塞算法。如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被成为无锁(Lock-Free)算法。如果在算法中仅将CAS用于协调线程之间的操作,并且能正确实现,那么这它既是一种无阻塞算法,又是一种无锁算法。
15.4.1 非阻塞的栈
非阻塞算法通常比基于锁的算法更为复杂。创建非阻塞算法的关键在于,找出如何将原子修改的范围缩小到单个变量上,同时还要维护数据的一致性。程序清单15-6的ConcurrentStack中给出了如何通过原子引用来构建栈的示例。



15.4.2 非阻塞的链表

链接队列需要单独维护头指针和尾指针。有两个指针指向位于尾部的节点:当前最后一个元素的next指针,以及尾节点。当成功插入一个元素时,这两个指针都要采用原子操作来更新。初看起来,这个操作无法通过原子变量来实现。在更新这两个指针时需要不同的CAS操作,并且如果第一个CAS成功,但第二个CAS失败,那么队列将处于不一致的状态。而且即使这两个CAS都成功了,那么在执行这两个CAS之间,仍可能有另一个线程会访问这个队列。


程序15-7的LinkedQueue中给出了Michael-Scott提出的非阻塞链接队列算法中的插入部分,在ConcurrentLinkedQueue中使用的正是该算法。在许多队列算法中,空队列通常都包含一个“哨兵”节点或者“哑”节点,并且头节点和尾节点在初始化都指向该哨兵节点。尾节点通常要么指向哨兵节点(如果队列为空),即队列的最后一个元素,要么(当有操作正在执行更新时)指向倒数第二个元素。



这里的重点是理解那个“中间状态”以及“稳定状态”。(上面的图印刷错了,是队列不是对立)。上面操作中的“中间状态”如图15-4所示:

当第二次更新完成后(更新尾节点指针),队列将再次处于稳定状态,如图15-5所示:



LinkedQueue.put方法在插入新元素之前,将首先检查队列是否处于中间状态(步骤A)。如果是,那么有另一个线程正在插入元素(在步骤C和D)之间。此时当前线程不会等待其他线程执行完成,而是帮助它完成操作,并将尾节点向前推进一个节点(步骤B)。然后,它将重复执行这种检查,以免另一个线程已经开始插入元素,并继续推进尾节点,知道它发现队列处于稳定状态之后,才会开始执行自己的插入操作。
15.4.3 原子域的更新器


原子的域更新器类表示现有volatile域的一种基于反射的“视图”,从而能够在已有的volatile域上使用CAS。在更新器类中没有构造函数,要创建一个更新器对象,可以调用newUpdater工厂方法,并定制类和域的名字。域更新器没有与某个特定的实例关联在一起,因而可以更新目标类的任意实例中的域。它提供的原子保证性比普通的原子类更弱一些。在ConcurrentLinkedQueue,使用nextUpdater的compareAndSet方法来更新Node中的next域,完全是为了提升性能。对于一些频繁分配并且生命周期短暂的对象,如队列中的链接节点,如果能去掉每个Node的AtomicRefrence创建过程,那么将极大地降低插入操作的开销。然而,几乎在所有情况下,普通原子变量的性能都很不错,只有在很少的情况下才会使用原子的域更新器。(如果在执行原子更新的同时还需要维持现有类的串行化形式,那么原子的域更新器将非常有用)
15.4.4 ABA问题

如果在算法中采用自己的方式来管理节点对象的内存,那么可能出现ABA问题。一个简单的解决方案是:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号。即使这个值有A变为B,然后又变为A,版本号也将是不同的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。
小结:

《java并发编程实战》读书笔记12--原子变量,非阻塞算法,CAS的更多相关文章
- Java并发编程实战 读书笔记(一)
最近在看多线程经典书籍Java并发变成实战,很多概念有疑惑,虽然工作中很少用到多线程,但觉得还是自己太弱了.加油.记一些随笔.下面简单介绍一下线程. 一 线程与进程 进程与线程的解释 个人觉 ...
- Java并发编程实战 第15章 原子变量和非阻塞同步机制
非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatil ...
- Java并发编程实战 读书笔记(二)
关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...
- 《java并发编程实战》笔记
<java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为: Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...
- Java多线程编程实战读书笔记(一)
多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
- 《Java并发编程实战》笔记-非阻塞算法
如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...
- 《Java并发编程实战》笔记-锁与原子变量性能比较
如果线程本地的计算量较少,那么在锁和原子变量上的竞争将非常激烈.如果线程本地的计算量较多,那么在锁和原子变量上的竞争会降低,因为在线程中访问锁和原子变量的频率将降低. 在高度竞争的情况下,锁的性能将超 ...
- 《Java并发编程实战》笔记-OneValueCache与原子引用技术
/** * NumberRange * <p/> * Number range class that does not sufficiently protect its invariant ...
- Java并发编程艺术读书笔记
1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...
- Java并发编程实践读书笔记(5) 线程池的使用
Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到 ...
随机推荐
- 【队列】【P2827】【NOIP2016D2T3】蚯蚓
传送门 Description 本题中,我们将用符号 $\lfloor c \rfloor$ 表示对 $c$ 向下取整,例如:$\lfloor 3.0 \rfloor = \lfloor 3.1 \r ...
- 背景建模技术(四):视频分析(VideoAnalysis)模块
视频分析模块主要包含两个函数,一个是VideoAnalysis::setup(....),其主要功能就是确定测试的视频是视频文件或摄像头输入亦或是采用命令行参数:第二个函数是VideoAnalysis ...
- 使用VS2010编译MongoDB C++驱动详解
最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...
- 寻找最大连续子序列/Find the max contiguous subsequence
寻找最大连续子序列 给定一个实数序列X1,X2,...Xn(不需要是正数),寻找一个(连续的)子序列Xi,Xi+1,...Xj,使得其数值之和在所有的连续子序列数值之和中为最大. 一般称这个子序列为最 ...
- DataGridView导出到Word
#region 使用Interop.Word.dll将DataGridView导出到Word /// <summary> /// 使用Interop.Word.dll将DataGridVi ...
- vijos 1426 背包+hash
背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
- [洛谷P1527] [国家集训队]矩阵乘法
洛谷题目链接:[国家集训队]矩阵乘法 题目背景 原 <补丁VS错误>请前往P2761 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入 ...
- [SDOI2008]仪仗队 (洛谷P2158)
洛谷题目链接:[SDOI2008]仪仗队 题目描述 作为体育委员,C君负责这次运动会仪仗队的训练.仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视 ...
- Send Email in Robot Framework Python Using Gmail
转载自:http://seleniummaster.com/sitecontent/index.php/selenium-robot-framework-menu/selenium-robot-fra ...
- bzoj3172 Ac自动机
根据fail树的性质 我们在建树的时候每建一个串就将他路径上的点全部加1表示这个串的后缀又出现了一次 然后从下到上把sum加起来就可以得到答案了 #include<cstdio> #inc ...