了解CAS(Compare-And-Swap)

CAS即对比交换,它在保证数据原子性的前提下尽可能的减少了锁的使用,很多编程语言或者系统实现上都大量的使用了CAS。

JAVA中CAS的实现

JAVA中的cas主要使用的是Unsafe方法,Unsafe的CAS操作主要是基于硬件平台的汇编指令,目前的处理器基本都支持CAS,只不过不同的厂家的实现不一样罢了。

Unsafe提供了三个方法用于CAS操作,分别是

public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);

public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);

public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);

复制代码

value 表示 需要操作的对象

valueOffset 表示 对象(value)的地址的偏移量(通过Unsafe.objectFieldOffset(Field valueField)获取)

expect 表示更新时value的期待值

update 表示将要更新的值

具体过程为每次在执行CAS操作时,线程会根据valueOffset去内存中获取当前值去跟expect的值做对比如果一致则修改并返回true,如果不一致说明有别的线程也在修改此对象的值,则返回false

Unsafe类中compareAndSwapInt的具体实现:

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))

UnsafeWrapper("Unsafe_CompareAndSwapInt");

oop p = JNIHandles::resolve(obj);

jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);

return (jint)(Atomic::cmpxchg(x, addr, e)) == e;

UNSAFE_END

复制代码

ABA问题

线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。

aba.png

例子

public static AtomicInteger a = new AtomicInteger(1);

public static void main(String[] args){

Thread main = new Thread(() -> {

System.out.println("操作线程" + Thread.currentThread() +",初始值 = " + a); //定义变量 a = 1

try {

Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行

} catch (InterruptedException e) {

e.printStackTrace();

}

boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作

System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);

},"主操作线程");

Thread other = new Thread(() -> {

a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2

System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ a);

a.decrementAndGet(); // a 减 1, a - 1 = 2 - 1 = 1

System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);

},"干扰线程");

main.start();

other.start();

}

复制代码

// 输出

> 操作线程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问题。

//关键代码

public class AtomicStampedReference {

private static class Pair {

final T reference; //维护对象引用

final int stamp; //用于标志版本

private Pair(T reference, int stamp) {

this.reference = reference;

this.stamp = stamp;

}

static Pair of(T reference, int stamp) {

return new Pair(reference, stamp);

}

}

private volatile Pair pair;

....

/**

* expectedReference :更新之前的原始值

* newReference : 将要更新的新值

* expectedStamp : 期待更新的标志版本

* newStamp : 将要更新的标志版本

*/

public boolean compareAndSet(V expectedReference,

V newReference,

int expectedStamp,

int newStamp) {

Pair 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

}

}

复制代码

例子

private static AtomicStampedReference 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(() -> {

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],【increment】 ,值 = 2

> 操作线程Thread[干扰线程,5,main],【decrement】 ,值 = 1

> 操作线程Thread[主操作线程,5,main],初始值 a = 1

> 操作线程Thread[主操作线程,5,main],CAS操作结果: true

复制代码

java 线程aba,JAVA中CAS-ABA的问题解决方案AtomicStampedReference相关推荐

  1. 什么是java线程?java线程模型的组成

    关于java线程的文章早已是非常多了,本文是对我个人过往学习java,理解及应用java线程的一个总结.此文内容涉及java线程的基本概念,以及什么是java线程等相关问题,希望对大家有所帮助. 什么 ...

  2. Java线程池ExecutorService中重要的方法

    ExecutorService 介绍 ExecutorService是java线程池定义的一个接口,它在java.util.concurrent包中,在这个接口中定义了和后台任务执行相关的方法. Ja ...

  3. java 线程 cpu_java程序中线程cpu使用率计算

    最近确实遇到题目上的刚需,也是花了一段时间来思考这个问题. cpu使用率如何计算 计算使用率在上学那会就经常算,不过往往计算的是整个程序执行的时间段,现在突然要实时计算还真有点无奈,时间段如何选择是个 ...

  4. java 线程可见性,Java多线程之内存可见性

    Java内存模型( JMM ) : 1) 所有的变量都存储在主内存中 2) 每个线程都有自己独立的工作内存, 里面保存该线程使用到的变量的副本 ( 主内存中该变量的一份拷贝 ) JMM两条规定: 1) ...

  5. Java线程、Java多线程详细介绍

    目录 一.进程和线程的区别 1.1 进程 1.2 线程 二.并发和并行 2.1 并行 2.2  并发 2.3 监控线程的执行情况 三.创建方式 3.1 继承Thread类 思考:为什么不直接通过对象调 ...

  6. java线程callback,Java线程之异步回调(Callback)

    ●介绍 有时候执行一个任务需要很长时间,单线程下线程会处于阻塞状态.这个时候我们会考虑一种非阻塞的处理模式.非阻塞任务在任何编程语言里都必不可少,Java也不例外.多线程就是一个很好的解决办法. 但是 ...

  7. 求一个简单的java线程代码,Java线程代码的实现方法

    1.继承Thread 声明Thread的子类 运行thread子类的方法 2.创建Thread的匿名子类 3.实现Runnable接口 声明 运行 4.创建实现Runnable接口的匿名类 5.线程名 ...

  8. java线程状态——java线程状态图

    java线程状态图,网上有很多资源,无外乎两种,如下: 图一 图二 图二是比较多的,可以看出来我贴出的链接这个博主挺有心的,还把右下角的水印给抹去了,赤裸裸的伪原创心理呀. 还有几张图说的也挺不错的, ...

  9. java线程状态(Java线程状态和系统线程)

    线程有几种状态? 一般说有3种,但也有说4种的 就绪:线程分配了CPU以外的全部资源,等待获得CPU调度 执行:线程获得CPU,正在执行 阻塞:线程由于发生I/O或者其他的操作导致无法继续执行,就放弃 ...

  10. qotd服务_QOTD:Java线程与Java堆空间

    qotd服务 以下问题很常见,并且与OutOfMemoryError有关:在JVM线程创建过程和JVM线程容量期间无法创建新的本机线程问题. 这也是我向新技术候选人(高级职位)提出的典型面试问题. 我 ...

最新文章

  1. JSP中是EL表达式与JSTL
  2. JAVA wait(), notify(),sleep具体解释
  3. Linux简单的http服务器:SimpleHTTPServer
  4. css3 手机信号,CSS3 无线路由器连接信号动画
  5. Mybatis 中更新方法: updateByPrimaryKeySelective() 和 updateByPrimaryKey() 的区别
  6. vant状态页组件van-empty - 使用篇
  7. 【EOS】EOS环境基础
  8. poi实现百万级数据导出
  9. 苹果手机关于推送的查看LOG的一种解决办法
  10. Map使用put进行数据的添加,对哈希表的三步添加的步骤
  11. 7-1 六度空间(30 分)
  12. 仪器仪表通讯协议1: CJ/T188水表通讯协议
  13. 130 个相见恨晚的超实用网站
  14. Gram矩阵简单解释
  15. 什么是知识,什么是知识图谱,有什么作用,有哪些应用领域?
  16. 【云端软件】竞价实例(Spot instance):可节省80%-90%的云计算成本
  17. 头脑王者对战游戏html5源码,头脑王者分析笔记及微信小程序解包源码
  18. MOS管基本驱动电路
  19. Android中解析读取复杂word,excel,ppt等的方法
  20. 怎么设置服务器文件夹多权限管理,如何设置共享文件夹,小编告诉你如何设置共享文件夹权限...

热门文章

  1. 最全解释P2P、P2C 、O2O 、B2C、B2B、 C2C的定义
  2. jquery插件---自动补全类插件
  3. .NET三种事务处理详解
  4. 2020年PMCAFF大咖分享合集(有回放)
  5. 为什么产品经理总在焦虑
  6. 透过现象看本质,如何设计一款病毒式裂变产品?
  7. 比尔盖茨,马斯克、霍金都告诉你:为什么要警惕人工智能(中)
  8. 【干货】原型设计的八大指导原则
  9. [新产品发布|推广] 赠书活动来了,参与有机会得《产品经理那些事儿》图书!...
  10. Spring【AOP模块】就是这么简单