java CAS
Compare and Swap
在这里,CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。简单介绍一下这个指令的操作过程:首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我原来的值是多少”(这段描述引自《Java Concurrency in Practice》)
Compare and Set
在理解了什么是 Compare and Swap 之后,理解 Compare and Set 便很容易了。Compare and Set 就是利用 Compare and Swap 实现的非阻塞的线程安全的写操作算法。它的实现过程如下:首先读取你要更改的数据的原值,然后将其和你要更新成的值作为 Compare and Swap 操作的两个参数,如果 Compare and Swap 的返回值和原值不同,便重复这一过程,直至成功。写成伪代码如下
int old;
int new;
do {
old = value.get();
new = doSomeCalcBasedOn(old)
while (value.compareAndSwap(old, new));
Compare and Set 是一个非阻塞的算法,这是它的优势。但是有一个问题就是存在这样的可能,在每次读操作之后,写操作之前,都有另外一个线程更改了原先的值,这样 Compare and Set 操作就会不停地重试。但这样的可能只存在于理论,在实际中很少发生。
public final int getAndIncrement() {
for (;;) {
int current = get();//先取出当前时间点的值
int next = current + 1;
if (compareAndSet(current, next))//然后尝试赋新值,在赋值时判断这个值是否改变。
return current;
}
}
public final boolean compareAndSet (int expect, int update) {
return unsafe.compareAndSwapInt( this, valueOffset, expect, update);
}
ABA问题:
CAS操作容易导致ABA问题,也就是在做a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。
在运用CAS做Lock-Free操作中有一个经典的ABA问题:
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,
例如下面的例子:

现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B:
head.compareAndSet(A,B);
在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态:


其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。
以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference<E>也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题
例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新,AtomicInteger会成功执行CAS操作,而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.concurrent.atomic.AtomicStampedReference; public class ABA {
private static AtomicInteger atomicInt = new AtomicInteger(100);
private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0); public static void main(String[] args) throws InterruptedException {
Thread intT1 = new Thread( new Runnable() {
@Override
public void run() {
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
}
}); Thread intT2 = new Thread( new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
boolean c3 = atomicInt.compareAndSet(100, 101);
System. out.println(c3); // true
}
}); intT1.start();
intT2.start();
intT1.join(); //join方法保证intT1执行完毕后执行intT2
intT2.join(); Thread refT1 = new Thread( new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
}
}); Thread refT2 = new Thread( new Runnable() {
@Override
public void run() {
int stamp = atomicStampedRef.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System. out.println(c3); // false
}
}); refT1.start();
refT2.start();
refT1.join();
refT2.join();
}
}
java CAS的更多相关文章
- JAVA CAS原理深度分析(转)
看了一堆文章,终于把JAVA CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...
- JAVA CAS原理深度分析 volatile,偏向锁,轻量级锁
JAVA CAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 偏向锁,轻量级锁 https://blog.csdn.net/zqz ...
- 转:JAVA CAS原理深度分析
看了一堆文章,终于把Java CAS的原理深入分析清楚了. 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/ ...
- Java CAS 原理详解
1. 背景 在JDK 5之前Java语言是靠 synchronized 关键字保证同步的,这会导致有锁.锁机制存在以下问题: 在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问 ...
- JAVA CAS原理深度分析-转载
参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...
- JAVA CAS原理
转自: http://blog.csdn.net/hsuxu/article/details/9467651 CAS CAS: Compare and Swap java.util.concurren ...
- Java CAS 和ABA问题
独占锁:是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁. 乐观锁:每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功 ...
- 【转】JAVA CAS原理深度分析
java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...
- JAVA CAS原理深度分析
参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html http://blog.hesey.net/2011/09/reso ...
随机推荐
- 批量插入使用SqlBulkCopy
对于大量的数据插入,我们可以使用批量插入功能来提升性能,例如.
- PULL解析
PULL解析类似于SAX解析,都采用事件驱动(利用getEventType()方法)方式进行解析,当PULL解析器开始解析之后,可以不断地调用PULL解析器的next()方法获取下一个解析事件(开始文 ...
- call,apply,bind函数
一.call函数 a.call(b); 简单的理解:把a对象的方法应用到b对象上(a里如果有this,会指向b) call()的用法:用在函数上面 var Dog=function(){ this.n ...
- 转: html表单中get方式和post方式的区别
1.Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据. 2.Get将表单中数据的按照variable=value的形式,添加到action所指向的URL后面,并且两者使用“?”连接 ...
- bzoj 1856: [Scoi2010]字符串
#include<cstdio> #include<iostream> #define Q 20100403 ; int main() { scanf("%lld%l ...
- 二模 (5)day1
第一题: 题目大意:解一元一次方程(只有+-符号): 解题过程:直接处理处两边的x的系数和常数项,字符串的处理即可. 第二题: 题目大意:求逆序对数. 解题过程:直接归并排序. 第三题: 题目大意:多 ...
- 一模 (1) day1
第一题:(水题) 题目大意:求出n个 X% (X是小于等于2位的整数) 的乘积,去掉末尾的0: 解题过程: 1.直接 把整数乘好,然后确定小数点的位置,去掉多余的0 输出即可. 第二题:(搜索题) ...
- [开发笔记]-“在引用COM组件时,出现了无法嵌入互操作类型。。。”的错误
这两天在做一个需要将wps文档转换成word文档的程序,在调用wps的com组件时项目编译是没有问题的,但当运行的时候却弹出了下面的错误提示: 从网上百度一番后,找到了正确的解决方法. 先从Com组件 ...
- swing LayoutManager 和多态
interface LayoutManager{ void show();}class FlowLayout implements LayoutManager{ public void show(){ ...
- jQuery tab plugin
/* www.keleyi.com/ */ ; (function ($) { $.fn.extend({ Tabs: function (options) { // 处理参数 options = $ ...