java atomic cas_Java-CAS 与 Atomic

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 voidincrement() {

count++;

}public intgetCount() {returncount;

}

使用 AtomicInteger 后:

private AtomicInteger count = newAtomicInteger();public voidincrement() {

count.incrementAndGet();

}//使用 AtomicInteger 后,不需要加锁,也可以实现线程安全

public intgetCount() {returncount.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 intincrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;

}public final int getAndAddInt(Object o, long offset, intdelta) {intv;do{

v=getIntVolatile(o, offset);

}while (!compareAndSwapInt(o, offset, v, v +delta));returnv;

}public native int getIntVolatile(Object o, longoffset);public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

最后调用的是 Unsafe 类的方法,主要是 compareAndSwapInt 方法,即 CAS。

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 为内存地址。

#include "runtime/atomic.inline.hpp"jbyte Atomic::cmpxchg(jbyte exchange_value,volatile jbyte*dest, jbyte compare_value) {

assert(sizeof(jbyte) == 1, "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;

}returncur_as_bytes[offset];

}

inline jint Atomic::cmpxchg (jint exchange_value, volatile jint*dest, jint compare_value) {int mp =os::is_MP();

__asm__volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)":"=a"(exchange_value)

:"r" (exchange_value), "a" (compare_value), "r" (dest), "r"(mp)

:"cc", "memory");returnexchange_value;

}

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 _emit0xF0\

__asm L0:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值