Java-CAS 与原子类
CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术。
CAS 的思想很简单:三个参数,一个当前内存值 V、旧的预期值 A、即将更新的值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值修改为 B 并返回 true,否则什么都不做,并返回 false。
和 CAS 相关的一个概念是原子操作。原子操作是不可被中断的一个或一系列操作。而 CAS 则是 Java 中保证原子操作的一种方式。
从 Java1.5 开始,JDK 的并发包里就提供了一些类来支持原子操作,都是以 Atomic 开头。
volatile 不能保证类似 i++ 这样操作的原子性,CAS 能够保证。
一、原子类使用
以 AtomicInteger 为例,常用 API:
- public final int get():获取当前的值
- public final int getAndSet(int newValue):获取当前的值,并设置新的值
- public final int getAndIncrement():获取当前的值,并自增
- public final int getAndDecrement():获取当前的值,并自减
- public final int getAndAdd(int delta):获取当前的值,并加上预期的值
相比 Integer 的优势,多线程中让变量自增:
private volatile int count = 0;
// 若要线程安全执行执行 count++,需要加锁
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
使用 AtomicInteger 后:
private AtomicInteger count = new AtomicInteger();
public void increment() {
count.incrementAndGet();
}
// 使用 AtomicInteger 后,不需要加锁,也可以实现线程安全
public int getCount() {
return count.get();
}
二、CAS 问题
CAS 方式为乐观锁,synchronized 为悲观锁。因此使用 CAS 解决并发问题通常情况下性能更优。
但使用 CAS 方式也会有几个问题:
1.循环时间长开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
2.只能保证一个共享变量的原子操作
对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就需要用锁。
从 Java 1.5 开始,JDK 提供了 AtomicReference 类来保证引用对象之间的原子性。
3.ABA 问题
如果一个值原来是 A,变成了 B,又变成了 A,那么使用 CAS 进行检查时则会发现它的值没有发生变化,但是实际上却变化了。
解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么 A->B->A 就会变成 1A->2B->3A。
从 Java 1.5 开始,JDK 提供了 AtomicStampedReference、AtomicMarkableReference 类来解决 ABA 问题。
三、CAS 实现
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
public native int getIntVolatile(Object o, long offset);
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
最后调用的是 Unsafe 类的方法,主要是 compareAndSwapInt 方法,即 CAS。
查看 Unsafe 的实现:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/prims/unsafe.cpp
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
其中核心方法为 Atomic::cmpxchg(x, addr, e),其中参数 x 是即将更新的值,参数 e 是原内存的值,参数 addr 为内存地址。
查看 Atomic 的实现:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/runtime/atomic.cpp
#include "runtime/atomic.inline.hpp"
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
assert(sizeof(jbyte) == , "assumption.");
uintptr_t dest_addr = (uintptr_t)dest;
uintptr_t offset = dest_addr % sizeof(jint);
volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
jint cur = *dest_int;
jbyte* cur_as_bytes = (jbyte*)(&cur);
jint new_val = cur;
jbyte* new_val_as_bytes = (jbyte*)(&new_val);
new_val_as_bytes[offset] = exchange_value;
while (cur_as_bytes[offset] == compare_value) {
jint res = cmpxchg(new_val, dest_int, cur);
if (res == cur) break;
cur = res;
new_val = cur;
new_val_as_bytes[offset] = exchange_value;
}
return cur_as_bytes[offset];
}
查看 atomic_linux_x86.inline.hpp,不同系统、不同 CPU 有不同的实现:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/share/vm/runtime/atomic.inline.hpp
Linux 的 x86 实现:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
int mp = os::is_MP();
__asm__ volatile (LOCK_IF_MP(%) "cmpxchgl %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}
Windows 的 x86 实现:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/fea2c7f50ce8/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
// Adding a lock prefix to an instruction on MP machine
// VC++ doesn't like the lock prefix to be on a single line
// so we can't insert a label after the lock prefix.
// By emitting a lock prefix, we can define a label after it.
#define LOCK_IF_MP(mp) __asm cmp mp, 0 \
__asm je L0 \
__asm _emit 0xF0 \
__asm L0:
https://www.cnblogs.com/noKing/p/9094983.html
Java-CAS 与原子类的更多相关文章
- Java多线程系列——原子类的实现(CAS算法)
1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...
- Java线程--Atomic原子类使用
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11871241.html Java线程--Atomic原子类使用 package concurr ...
- Java多线程—JUC原子类
根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...
- 深入浅出Java并发包—原子类操作
我们知道,JDK1.5以后引入了并发包(java.util.concurrent)用于解决多CPU时代的并发问题,而并发包中的类大部分是基于Queue的并发类,Queue在大多数情况下使用了原子类(A ...
- java多线程系列 JUC原子类 CAS及原子类
根据数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicInteg ...
- java的原子类到底是啥?ABA,CAS又是些什么?
1)解决并发不是用锁就能解决吗,那SDK干嘛还要搞个原子类出来? 锁虽然能解决,但是加锁解锁始终还是对性能是有影响的,并且使用不当可能会造成死锁之类的问题. 2)原子类是怎样使用的,比如说我要实现一个 ...
- Java并发—原子类,java.util.concurrent.atomic包(转载)
原子类 Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中 的原子操作类提供了一种用法简单.性能高效.线程安全地更新一个变量 ...
- 【Java并发工具类】原子类
前言 为保证计数器中count=+1的原子性,我们在前面使用的都是synchronized互斥锁方案,加锁独占访问的方式未免太过霸道,于是我们来介绍另一种解决原子性问题的无锁方案:原子变量.在正式介绍 ...
- Java原子类中CAS的底层实现
Java原子类中CAS的底层实现 从Java到c++到汇编, 深入讲解cas的底层原理. 介绍原理前, 先来一个Demo 以AtomicBoolean类为例.先来一个调用cas的demo. 主线程在f ...
- Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。
精彩理解: https://www.jianshu.com/p/21be831e851e ; https://blog.csdn.net/heyutao007/article/details/19 ...
随机推荐
- vue学习(7)-路由抽离
cnpm i vue-router -S
- persistence.xml模板配置
1.右键创建的persistence.xml 2.选择2.0版本的模板 3.复制右侧代码,写入xml文件中 <?xml version="1.0" encoding=&quo ...
- 【Git】五、远程仓库
前面4节将的都是本地的git操作,这节开始讲合并到本地分支后,如何与远程仓库做交互 -------------------------------- 提要 //生成本地ssh密钥 $ ssh-keyg ...
- Azkaban无法启动错误Error: Could not find or load main class 12321
1 错误日志 Using Hadoop from /mnt/software/hadoop-2.6.0-cdh5.16.1 bin/internal/../.. /mnt/software/jdk1. ...
- 07_Redis_Sorted Set命令
一:Redis 有序集合(sorted set):有序set集合,专门用来做排行榜 Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员 ------- (有序不重复) 不 ...
- Hyperscan简介
Hyperscan是一款来自于Intel的高性能的正则表达式匹配库. 参考 Hyperscan简介
- Almost Acyclic Graph CodeForces - 915D (思维+拓扑排序判环)
Almost Acyclic Graph CodeForces - 915D time limit per test 1 second memory limit per test 256 megaby ...
- oppo面经-java开发
Oppo一面(1)自我简介(2)介绍一个自己做过的最得意的项目,项目的细节,难点,怎么解决的,还存在的问题,有什么优化的想法吗(这个我说了很长时间,面试官说非计算机专业的,有这种实习经验确实能加分)( ...
- docker学习内容
有个博客写的蛮好的,转一下 https://blog.csdn.net/xiaochendefendoushi/article/details/80979905 等我用到的时候再仔细瞧瞧
- iOS RAC使用补充
1 延迟执行 [[RACScheduler mainThreadScheduler] afterDelay: schedule:^{ NSLog(@"延迟执行.."); }]; ...