Java 中 CAS
一、CAS 概念
CAS ,全称 Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制。
实现思想 CAS(V、A、B) ,V为内存地址,A为预期原值,B 为新值。如果内存地址的值与预期原址相匹配,那么将该位置值更新为新值。否则说明已经被其他线程更新,处理器不做任何处理。
无论哪种情况,它都会在 CAS 指令之前返回该位置的值。而我们可以使用自旋锁,循环CAS ,重新读取该变量再次尝试修改该变量,也可以放弃操作

CAS操作由处理器提供支持,是一种原语。原语是操作系统或计算机网络用语范畴。是由若干条指令组成的,用于完成一定功能的一个过程,具有不可分割性,即原语的执行必须是连续的,在执行过程中不允许被中断。如 Intel 处理器,比较并交换通过指令的 cmpxchg 系列实现。处理器相关指令不做过多介绍,有兴趣的可自行查阅资料。
二、JDK1.8 中的CAS
Unsafe类,在sun.misc包下,不属于Java标准。Unsafe类提供一系列增加Java语言能力的操作,如内存管理、操作类/对象/变量、多线程同步等。其中与CAS相关的方法有以下几个:
//var1为CAS操作的对象,offset为var1某个属性的地址偏移值,expected为期望值,var2为要设置的值,利用JNI来完成CPU指令的操作
public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2);
public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2);
public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);
/** 如果CAS成功,return oldValue, oldValue = oldValue + addValue
* 如果CAS失败,自旋,一直运行,直到成功为止
*/
public final Xxx getAndAddXxx(Object var1, long offset, long addValue) {
int oldValue;
do {
oldValue = this.getIntVolatile(var1, offset);
} while(!this.compareAndSwapInt(var1, offset, oldValue, oldValue + addValue)); return oldValue;
} /** 如果CAS成功,return oldValue, oldValue = newValue
* 如果CAS失败,自旋,一直运行,直到成功为止
*/
public final Xxx getAndSetXxx(Object var1, long offset, Object newValue) {
int oldValue;
do {
oldValue = this.getXxxVolatile(var1, offset);
} while(!this.compareAndSwapXxx(var1, offset, oldValue, newValue)); return oldValue;
}
一般不建议使用Unsafe类,除非对它有很深入的了解。
java.util.concurrent包中大量使用了CAS原理,如AtomicInteger类,都是调用上面几个Unsafe方法保证多线程数据的正确性
以下是AtomicInteger的CAS操作相关源码
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
// Unsafe类,提供一系列增强Java的功能,如内存管理、操作类/对象/变量、多线程同步等。不建议开发者调用
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 获取对象某个属性的地址偏移值
private static final long valueOffset;
static {
try {
// value相对“起始地址”的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// value值, volatile修饰,保证不同线程间的可见性
private volatile int value;
public AtomicInteger(int initialValue) { value = initialValue; }
public AtomicInteger() {}
public final int get() { return value; }
public final void set(int newValue) { value = newValue; }
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
//有序或者有延迟的putIntVolatile方法
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* Atomically sets to the given value and returns the old value.
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
// JNI调用,实现CAS
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* i++ 操作
* Atomically increments by one the current value.
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, );
}
/**
* i-- 操作
* Atomically decrements by one the current value.
* @return the previous value
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -);
}
/**
* return i, i = i + n 操作
* Atomically adds the given value to the current value.
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
/**
* ++i 操作
* Atomically increments by one the current value.
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, ) + ;
}
/**
* --i 操作
* Atomically decrements by one the current value.
* @return the updated value
*/
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -) - ;
}
/**
* i = i + n ,return i操作
* Atomically adds the given value to the current value.
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
// 其余函数,略...
三、CAS缺点
CAS有几个缺点:
1、ABA问题。当第一个线程执行CAS操作,尚未修改为新值之前,内存中的值已经被其他线程连续修改了两次,使得变量值经历 A -> B -> A的过程。
解决方案:添加版本号作为标识,每次修改变量值时,对应增加版本号; 做CAS操作前需要校验版本号。JDK1.5之后,新增AtomicStampedReference类来处理这种情况。
2、循环时间长开销大。如果有很多个线程并发,CAS自旋可能会长时间不成功,会增大CPU的执行开销。
3、只能对一个变量进原子操作。JDK1.5之后,新增AtomicReference类来处理这种情况,可以将多个变量放到一个对象中。
参考:https://www.cnblogs.com/Shuuichi/p/10590710.html
Java 中 CAS的更多相关文章
- Java中CAS原理详解
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...
- Java中CAS详解
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2 ...
- Java中CAS 基本实现原理
一.前言 了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.a ...
- Java中CAS原理分析(volatile和synchronized浅析)
CAS是什么? CAS英文解释是比较和交换,是cpu底层的源语,是解决共享变量原子性实现方案,它定义了三个变量,内存地址值对应V,期待值E和要修改的值U,如下图所示,这些变量都是在高速缓存中的,如果两 ...
- Java中CAS 基本实现原理 和 AQS 原理
一.前言了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.at ...
- 详解java中CAS机制所导致的问题以及解决——内存顺序冲突
[CAS机制] 指的是CompareAndSwap或CompareAndSet,是一个原子操作,实现此机制的原子类记录着当前值的在内存中存储的偏移地址,将内存中的真实值V与旧的预期值A做比较,如果不一 ...
- 沉淀再出发:java中的CAS和ABA问题整理
沉淀再出发:java中的CAS和ABA问题整理 一.前言 在多并发程序设计之中,我们不得不面对并发.互斥.竞争.死锁.资源抢占等等问题,归根到底就是读写的问题,有了读写才有了增删改查,才有了所有的一切 ...
- 转:Java中的cas
引自:https://blog.csdn.net/mmoren/article/details/79185862 本篇的思路是先阐明无锁执行者CAS的核心算法原理然后分析Java执行CAS的实践者Un ...
- 【Java】CAS的乐观锁实现之AtomicInteger源码分析
1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...
随机推荐
- layer iframe 设置关闭按钮 和刷新
layer.open({ type: 2, title: 'XXXX网吧历史更多数据', shade:0, // closeBtn:0, resize:false, move:false, shade ...
- [NOI2010] 超级钢琴 - 贪心,堆,ST表
这也算是第K大问题的套路题了(虽然我一开始还想了个假算法),大体想法就是先弄出最优的一批解,然后每次从中提出一个最优解并转移到一个次优解,用优先队列维护这个过程即可. 类似的问题很多,放在序列上的,放 ...
- 【转】mathnet 使用方法介绍
转载自:http://blog.csdn.net/c914620529/article/details/50393223 在C#中使用mathnet,需要利用using引入相关类 矩阵运算的相关类: ...
- SQLServer2008不允许保存更改错误解决办法
SQLServer2008不允许保存更改错误解决办法 今天在运行sql server 2008时候提示不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法. 一.启动SQL Server ...
- AcWing 8.二维费用的背包问题
#include<iostream> #include<algorithm> #include<cstring> using namespace std ; ; i ...
- rancher说明为什么需要按照指定版本安装以及rancher和节点linux环境配置-docker指定版本安装
rancher说明为什么需要按照指定版本安装以及rancher和节点linux环境配置-docker指定版本安装 待办 https://blog.csdn.net/CSDN_duomaomao/art ...
- es 6.x scroll用法
我们可以使用from +size来获取所有数据,但是,如果数据量大的时候,这样的操作开销很大,这时候可以使用scroll操作 1.第一步发起一个scroll 的post请求,带上参数scroll=1m ...
- HTML5-语义化
什么是语义化?就是用合理.正确的标签来展示内容,比如h1~h6定义标题. 语义化优点: 易于用户阅读,样式丢失的时候能让页面呈现清晰的结构. 有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权 ...
- winform学习(1)初识winform
winform是Windows窗体应用程序 在窗体设计界面 单击鼠标右键--查看代码,即可转到Form1.cs的代码界面 从代码界面转到窗体设计界面的三种快捷方法:①双击解决方案资源管理器的 For ...
- POJ 2018 Best Cow Fences(二分答案)
题目链接:http://poj.org/problem?id=2018 题目给了一些农场,每个农场有一定数量的奶牛,农场依次排列,问选择至少连续排列F个农场的序列,使这些农场的奶牛平均数量最大,求最大 ...