Java中CAS-ABA的问题解决方案
忻州SEO摘要
CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS。
了解CAS(Compare-And-Swap)
CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS。
JAVA中CAS的实现
JAVA中的cas主要使用的是Unsafe方法,Unsafe的CAS操作主要是基于硬件平台的汇编指令,目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。
Unsafe提供了三个方法用于CAS操作,分别是
|
1
|
<p style="line-height: 1.5em;">public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);<br>public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);<br>public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);<br></p> |
value 表示 需要操作的对象
valueOffset 表示 对象(value)的地址的偏移量(通过
Unsafe.objectFieldOffset(Field valueField)获取)expect 表示更新时value的期待值
update 表示将要更新的值
具体过程为每次在执行CAS操作时,线程会根据valueOffset去内存中获取当前值去跟expect的值做对比如果一致则修改并返回true,如果不一致说明有别的线程也在修改此对象的值,则返回false
Unsafe类中compareAndSwapInt的具体实现:
|
1
|
<p style="line-height: 1.5em;">UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))<br> UnsafeWrapper("Unsafe_CompareAndSwapInt");<br> oop p = JNIHandles::resolve(obj);<br> jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); return (jint)(Atomic::cmpxchg(x, addr, e)) == e;<br>UNSAFE_END<br></p> |
ABA问题
线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。

aba.png
例子
|
1
|
<p style="line-height: 1.5em;">public static AtomicInteger a = new AtomicInteger(1);public static void main(String[] args){<br> Thread main = new Thread(() -> {<br> System.out.println("操作线程" + Thread.currentThread() +",初始值 = " + a); //定义变量 a = 1<br> try {<br> Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行<br> } catch (InterruptedException e) {<br> e.printStackTrace();<br> } boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作<br> System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);<br> },"主操作线程");<br><br> Thread other = new Thread(() -> {<br> Thread.yield(); //确保thread-main线程优先执行<br> a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2<br> System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ a);<br> a.decrementAndGet(); // a 减 1, a - 1 = 2 - 1 = 1<br> System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);<br> },"干扰线程");<br><br> main.start();<br> other.start();<br>}<br></p> |
输出> 操作线程Thread[主操作线程,5,main],初始值 = 1>
操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2>
操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1>
操作线程Thread[主操作线程,5,main],CAS操作结果: true
解决ABA方案
思路
解决ABA最简单的方案就是给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号。

aba_2.png
JAVA中ABA中解决方案(AtomicStampedReference)
AtomicStampedReference主要维护包含一个对象引用以及一个可以自动更新的整数"stamp"的pair对象来解决ABA问题。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
//关键代码public class AtomicStampedReference<V> { private static class Pair<T> { final T reference; //维护对象引用 final int stamp; //用于标志版本 private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); } } private volatile Pair<V> pair; .... /** * expectedReference :更新之前的原始值 * newReference : 将要更新的新值 * expectedStamp : 期待更新的标志版本 * newStamp : 将要更新的标志版本 */ public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; //获取当前pair return expectedReference == current.reference && //原始值等于当前pair的值引用,说明值未变化 expectedStamp == current.stamp && // 原始标记版本等于当前pair的标记版本,说明标记未变化 ((newReference == current.reference && newStamp == current.stamp) || // 将要更新的值和标记都没有变化 casPair(current, Pair.of(newReference, newStamp))); // cas 更新pair }} |
例子
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(1, 0);public static void main(String[] args){ Thread main = new Thread(() -> { System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference()); int stamp = atomicStampedRef.getStamp(); //获取当前标识别 try { Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行 } catch (InterruptedException e) { e.printStackTrace(); } boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1); //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败 System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess); },"主操作线程"); Thread other = new Thread(() -> { Thread.yield(); // 确保thread-main 优先执行atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1); System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ atomicStampedRef.getReference()); atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1); System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference()); },"干扰线程"); main.start(); other.start();} |
// 输出
> 操作线程Thread[主操作线程,5,main],初始值 a = 2
> 操作线程Thread[干扰线程,5,main],【increment】 ,值 = 2
> 操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1
> 操作线程Thread[主操作线程,5,main],CAS操作结果: false
来源:内蒙古SEO
Java中CAS-ABA的问题解决方案的更多相关文章
- 计算价格, java中浮点数精度丢失的解决方案
计算价格, java中浮点数精度丢失的解决方案
- Java中CAS原理分析(volatile和synchronized浅析)
CAS是什么? CAS英文解释是比较和交换,是cpu底层的源语,是解决共享变量原子性实现方案,它定义了三个变量,内存地址值对应V,期待值E和要修改的值U,如下图所示,这些变量都是在高速缓存中的,如果两 ...
- Java中CAS原理详解
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...
- Java中CAS详解
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...
- CAS -- ABA问题的解决方案
我们现在来说什么是ABA问题.假设内存中有一个值为A的变量,存储在地址V中. 此时有三个线程想使用CAS的方式更新这个变量的值,每个线程的执行时间有略微偏差.线程1和线程2已经获取当前值,线程3还未获 ...
- Java中CAS 基本实现原理
一.前言 了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.a ...
- Java中CAS 基本实现原理 和 AQS 原理
一.前言了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.at ...
- Java中各类Cache机制实现解决方案[来自CSDN]
摘要:在Java中,不同的类都有自己单独的Cache机制,实现的方法也可能有所不同,文章列举了Java中常见的各类Cache机制的实现方法,同时进行了综合的比较. 在Java中,不同的类都有自己单独的 ...
- Java 中 CAS
一.CAS 概念 CAS ,全称 Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制. 实现思想 CAS(V.A.B) ,V为内存地址,A为预期原值,B ...
- 详解java中CAS机制所导致的问题以及解决——内存顺序冲突
[CAS机制] 指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一 ...
随机推荐
- Deepin安装JavaFX
1.环境:JDK1.8(目前国内好用的版本里比较流行的是这个版本),deepin 15.11(linux内核是5.2.9,安装了大黄蜂驱动): 2.由于是用的JDK1.8,所以没法用最新的openjf ...
- [转帖]谷歌宣称首次实现量子优越性,IBM“不服”,中国同行咋看?
谷歌宣称首次实现量子优越性,IBM“不服”,中国同行咋看? 投递人 itwriter 发布于 2019-10-24 15:46 评论(7) 有306人阅读 原文链接 [收藏] « » https:// ...
- PAT甲级满分有感
时间轴: 2017年,数据结构加入了我的课程清单. 2018年12月,我从网易云课堂下载了数据结构的所有课程视频(学校里没有网,只能离线看),开始一刷.一刷只看了视频,基本没有做题,看到AVL树的时候 ...
- hdu 2643 rank 第二类斯特林数
题意:给定n个人,要求这n个人的所有可能排名情况,可以多个人并列(这个是关键). 题解:由于存在并列的问题,那么对于n个人,我们最多有n个排名,枚举一下1~n,累加一下就好.(注意这里是变种的斯特林数 ...
- vue-cli3.0 关闭eslint校验
1. 跟着课程学习vue高级训练营时,vue-cli老是报eslint校验错误,把它关了! 网上找到了图中这个写法,可是报错啊! 解决办法:把false改为true 参考:https://blog ...
- java常见排序算法
今天去面试的时候又考了排序算法,排序这个东西,你以为你懂了,但是真正去写的时候才会发现好多细节自己都模棱两可,我写着写着就全都乱了,回来之后赶紧重新写一遍. (1)冒泡排序 public void b ...
- jQuery 基础知识
一.序言 jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后的又一个优秀的JavaScript代码库(JavaScript框架).jQuery设计的宗旨是"W ...
- 【转载】C#使用Trim方法去除字符串前后的所有空格
在C#语言程序开发过程中,很多时候需要对字符串对象的前后空格进行去除,此时就需要使用到Trim()方法来实现这个功能,Trim()方法可以快速去除字符串前端和后端的所有空格. 例如有个字符:strin ...
- How to change SAPABAP1 schema password In HANA
Symptom How to change SAPABAP1 schema password Environment HANA 1.x HANA 2.x Resolution Shutdown the ...
- Pandas-数据处理-基础部分
有趣的事,Python永远不会缺席! 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/11014882.html jupyter 代码 ...