强军网网站建设,广州企业建站模板,中国重大新闻,汶上哪个广告公司做网站目录 Java 中 CAS 是如何实现的?
CAS 算法存在哪些问题?
ABA 问题
循环时间长开销大
只能保证一个共享变量的原子操作 Java 中 CAS 是如何实现的?
在 Java 中#xff0c;实现 CAS#xff08;Compare-And-Swap, 比较并交换#xff09;操作的一个关键类是Unsafe。
Un…目录 Java 中 CAS 是如何实现的?
CAS 算法存在哪些问题?
ABA 问题
循环时间长开销大
只能保证一个共享变量的原子操作 Java 中 CAS 是如何实现的?
在 Java 中实现 CASCompare-And-Swap, 比较并交换操作的一个关键类是Unsafe。
Unsafe类位于sun.misc包下是一个提供低级别、不安全操作的类。由于其强大的功能和潜在的危险性它通常用于 JVM 内部或一些需要极高性能和底层访问的库中而不推荐普通开发者在应用程序中使用。
sun.misc包下的Unsafe类提供了compareAndSwapObject、compareAndSwapInt、compareAndSwapLong方法来实现的对Object、int、long类型的 CAS 操作 /*** 以原子方式更新对象字段的值。*/boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);/*** 以原子方式更新 int 类型的对象字段的值。*/boolean compareAndSwapInt(Object o, long offset, int expected, int x);/*** 以原子方式更新 long 类型的对象字段的值。*/boolean compareAndSwapLong(Object o, long offset, long expected, long x); ForceInlinepublic final boolean compareAndSwapObject(Object o, long offset,Object expected,Object x) {return theInternalUnsafe.compareAndSetReference(o, offset, expected, x);}/*** Atomically updates Java variable to {code x} if it is currently* holding {code expected}.** pThis operation has memory semantics of a {code volatile} read* and write. Corresponds to C11 atomic_compare_exchange_strong.** return {code true} if successful*/ForceInlinepublic final boolean compareAndSwapInt(Object o, long offset,int expected,int x) {return theInternalUnsafe.compareAndSetInt(o, offset, expected, x);}/*** Atomically updates Java variable to {code x} if it is currently* holding {code expected}.** pThis operation has memory semantics of a {code volatile} read* and write. Corresponds to C11 atomic_compare_exchange_strong.** return {code true} if successful*/ForceInlinepublic final boolean compareAndSwapLong(Object o, long offset,long expected,long x) {return theInternalUnsafe.compareAndSetLong(o, offset, expected, x);}
Unsafe类中的 CAS 方法是native方法。native关键字表明这些方法是用本地代码通常是 C 或 C实现的而不是用 Java 实现的。这些方法直接调用底层的硬件指令来实现原子操作。也就是说Java 语言并没有直接用 Java 实现 CAS。
更准确点来说Java 中 CAS 是 C 内联汇编的形式实现的通过 JNIJava Native Interface 调用。因此CAS 的具体实现与操作系统以及 CPU 密切相关。
Atomic 类依赖于 CAS 乐观锁来保证其方法的原子性而不需要使用传统的锁机制如 synchronized 块或 ReentrantLock。
AtomicInteger是 Java 的原子类之一主要用于对 int 类型的变量进行原子操作它利用Unsafe类提供的低级别原子操作方法实现无锁的线程安全性。
AtomicInteger核心源码如下
// 原子地获取并增加整数值
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {// 以 volatile 方式获取对象 o 在内存偏移量 offset 处的整数值v getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, v delta));// 返回旧值return v;
} 可以看到getAndAddInt 使用了 do-while 循环在compareAndSwapInt操作失败时会不断重试直到成功。也就是说getAndAddInt方法会通过 compareAndSwapInt 方法来尝试更新 value 的值如果更新失败当前值在此期间被其他线程修改它会重新获取当前值并再次尝试更新直到操作成功。 由于 CAS 操作可能会因为并发冲突而失败因此通常会与while循环搭配使用在失败后不断重试直到操作成功。这就是 自旋锁机制 。
CAS 算法存在哪些问题?
ABA 问题是 CAS 算法最常见的问题。
ABA 问题
如果一个变量 V 初次读取的时候是 A 值并且在准备赋值的时候检查到它仍然是 A 值那我们就能说明它的值没有被其他线程修改过了吗很明显是不能的因为在这段时间它的值可能被改为其他值然后又改回 A那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 ABA问题。 ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。JDK 1.5 以后的 AtomicStampedReference 类就是用来解决 ABA 问题的其中的 compareAndSet() 方法就是首先检查当前引用是否等于预期引用并且当前标志是否等于预期标志如果全部相等则以原子方式将该引用和该标志的值设置为给定的更新值。 /*** Atomically sets the value of both the reference and stamp* to the given update values if the* current reference is {code } to the expected reference* and the current stamp is equal to the expected stamp.** param expectedReference the expected value of the reference* param newReference the new value for the reference* param expectedStamp the expected value of the stamp* param newStamp the new value for the stamp* return {code true} if successful*/public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) {PairV current pair;returnexpectedReference current.reference expectedStamp current.stamp ((newReference current.reference newStamp current.stamp) ||casPair(current, Pair.of(newReference, newStamp)));} 循环时间长开销大
CAS 经常会用到自旋操作来进行重试也就是不成功就一直循环执行直到成功。如果长时间不成功会给 CPU 带来非常大的执行开销。
如果 JVM 能够支持处理器提供的pause指令那么自旋操作的效率将有所提升。pause指令有两个重要作用
延迟流水线执行指令pause指令可以延迟指令的执行从而减少 CPU 的资源消耗。具体的延迟时间取决于处理器的实现版本在某些处理器上延迟时间可能为零。避免内存顺序冲突在退出循环时pause指令可以避免由于内存顺序冲突而导致的 CPU 流水线被清空从而提高 CPU 的执行效率。
只能保证一个共享变量的原子操作
CAS 操作仅能对单个共享变量有效。当需要操作多个共享变量时CAS 就显得无能为力。不过从 JDK 1.5 开始Java 提供了AtomicReference类这使得我们能够保证引用对象之间的原子性。通过将多个变量封装在一个对象中我们可以使用AtomicReference来执行 CAS 操作。