详解java中CAS机制所导致的问题以及解决——内存顺序冲突

CAS机制

指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一致则说明内存中的值被其他线程修改过了,返回false,否则将新值B存入内存。

Java内部是使用本地调用类unsafe实现的。

Java原子类底层原理就是采用CAS机制。

可能会出现什么问题

  1. aba问题:

线程1取出A之后被阻塞了,此时线程2把内存中A改为B,一系列操作后又改为A,此时线程1恢复执行,取内存中的A与手中的A做比较,发现没有变,继续执行。

然而此时俩A虽然可能是一样的,但是其实是被修改过的。例如,线程1需要替换的是一个栈顶,从A替换成B,但是执行前线程2抢占到时间片,对栈做出了一系列出栈操作,然后又将A入栈,此时线程1恢复,发现栈顶还是A,所以替换成B,但此时这个栈已经不是原来的栈了。

解决思路——版本号:

在比较的时候加入版本号的比较,每次修改时也修改版本号。

1.5开始的AtomicStampedReference就是采用了的版本号比较。

  1. 执行开销大:

CAS如果长时间不成功会一直自旋循环,会产生不少的执行开销。并且为了自旋结束时避免内存顺序冲突,CPU会对流水线进行重排,这样会严重影响cpu性能。

解决思路——pause指令:

pause指令能让自旋失败时cpu睡眠一小段时间再继续自旋,从而使得读操作的频率低很多,为解决内存顺序冲突而导致的流水线重排的代价也会小很多。

内存顺序冲突——当自旋锁快要释放的时候,持锁线程会有一个store命令,外面自旋的线程会发出各自的load命令,而此处并没任何 happen-before 排序,所以处理器是乱序执行,所以为了避免load出现在store之前此时会进行流水线清空再重排序,会严重影响cpu效率,Pause指令的作用就是减少并行load的数量,从而减少重排序时所耗时间。(不懂load和store可以去看看JMM(java内存模型)的资料)

  1. 只能保证一个共享变量的原子性操作:

解决思路——1.5开始的AtomicReference可以保证引用的原子性,可以把多变量放入对象中进行原子操作。

从自己word笔记中复制过来的,没图,而且格式很多有问题,有时间再补。

原文地址:https://www.cnblogs.com/Xieyang-blog/p/9305898.html

时间: 2024-11-01 13:11:21

详解java中CAS机制所导致的问题以及解决——内存顺序冲突的相关文章

详解Java中代码块和继承

本文发表于个人GitHub主页,原文请移步详解Java中代码块和继承 阅读. 概念 1.代码块 局部代码块 用于限定变量生命周期,及早释放,提高内存利用率 静态代码块 对类的数据进行初始化,仅仅只执行一次. 构造代码块 把多个构造方法中相同的代码可以放到这里,每个构造方法执行前,首先执行构造代码块. 2.继承 继承是已有的类中派生出新的类,新的类能够吸收已有类的数据属性和行为,并能扩展新的功能. 代码块的执行顺序 public class Test {    public String name

详解java垃圾回收机制(转)及finalize方法(转)

详细介绍Java垃圾回收机制 垃圾收集GC(Garbage Collection)是Java语言的核心技术之一,之前我们曾专门探讨过Java 7新增的垃圾回收器G1的新特性,但在JVM的内部运行机制上看,Java的垃圾回收原理与机制并未改变.垃圾收集的目的在于清除不再使用的对象.GC通过确定对象是否被活动对象引用来确定是否收集该对象.GC首先要判断该对象是否是时候可以收集.两种常用的方法是引用计数和对象引用遍历. 引用计数收集器 引用计数是垃圾收集器中的早期策略.在这种方法中,堆中每个对象(不是

【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

这一篇我们将会介绍java中try,catch,finally的用法 以下先给出try,catch用法: try { //需要被检测的异常代码 } catch(Exception e) { //异常处理,即处理异常代码 } 代码区如果有错误,就会返回所写异常的处理. 首先要清楚,如果没有try的话,出现异常会导致程序崩溃.而try则可以保证程序的正常运行下去,比如说: try { int i = 1/0; } catch(Exception e) { ........ } 一个计算的话,如果除数

干货——详解Java中的关键字

在平时编码中,我们可能只注意了这些static,final,volatile等关键字的使用,忽略了他们的细节,更深层次的意义. 本文总结了Java中所有常见的关键字以及一些例子. static 关键字 概述: 当static修饰类的属性或者方法时,那么就可以在没有创建对象的情况下使用该属性或方法. 静态块也是static的一个应用,用于初始化类时的一些操作. 静态方法和静态变量 划重点 被static修饰后的属性或者方法,使用时不需要new 一个类,用类.属性名或方法名访问. 比如java.la

详解Java中的访问控制修饰符(public, protected, default, private)

Java中的访问控制修饰符已经困惑笔者多时,其中较复杂的情况一直不能理解透彻.今天下定决心,系统.全面地研究Java中的访问控制修饰符的所有方面,并整理成这篇文章,希望有同样疑惑的读者读完后能有所收获.如果文章中出现错误,欢迎评论指出,共同交流~ 说在前面:这篇文章只研究Java中访问控制修饰符声明类的变量/方法的情况. 先抛出结论: * 成员变量/方法的访问权限 *                                        private        default  

牛刀小试 - 详解Java中的接口与内部类的使用

一.接口 接口的理解 Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现: 也就是说,接口自身自提供方法的基本声明,而不提供方法体:接口中声明的方法只能被实现该接口的子类所具体实现. 接口是Java中另一种非常重要的结构.因为Java不支持多继承,某种程度来说这也造成了一定的局限性. 所以接口允许多实现的特点弥补了类不能多继承的缺点.通常通过继承和接口的双重设计,可以既保持类的数据安全也变相实现了多继承. 接口的特点 使用关键字"interface&quo

详解Java中对象的软、弱和虚引用的区别

对于大部分的对象而言,程序里会有一个引用变量来引用该对象,这是最常见的引用方法.除此之外,java.lang.ref包下还提供了3个类:SoftReference.WeakReference和PhantomReference.它们分别代表了系统对对象的另外3中引用方式:软引用.弱引用和虚引用. Java中四种引用的区别和关联: 强引用.这是Java中最常见的引用方式.程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象.当一个对象被一个或者多个引用变量引用时,它处于

详解Java中的clone方法

转载自:http://blog.csdn.net/zhangjg_blog/article/details/18369201 Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有几种方式可以创建对象呢? 1 使用new操作符创建一个对象 2 使用clone方法复制一个对象 那么这两种方式有什么相同和不同呢? new操作符的本意是

详解Java中Map用法

Map以按键/数值对的形式存储数据,这里要特别说明( Map.Entry,是Map的内部类,它用来描述Map中的键/值对). Map是一个接口,我们平时多用它的实现类HashMap. 用例如下: public static void main(String args[]) { HashMap hashmap = new HashMap(); hashmap.put("Item0", "Value0"); hashmap.put("Item1",