【Java并发】CAS 机制详解和 ABA 问题的解决

【Java并发】CAS 机制详解和 ABA 问题的解决

目录

什么是 CAS

CAS 的工作原理

三个关键参数

CAS 操作基本步骤

CAS 的核心 UnSafe

CAS 的使用

CAS 优缺点

优点

缺点

什么是 ABA 问题

ABA 问题的解决

什么是 CAS

CAS(Compare-And-Swap)是一种用于在多线程编程中实现原子操作的技术,是硬件级别的原子操作,它比较内存中的某个值是否为预期值,如果是,则更新为新值,否则不做修改。

CAS 的工作原理

三个关键参数

内存位置(V):需要检查和更新的变量的内存地址。

预期原值(A):线程预期的变量当前值。

新值(B):如果变量的当前值与预期原值相匹配,那么将变量更新为这个新值。

CAS 操作基本步骤

比较:首先,线程会检查内存位置的值是否与预期原值相等。

交换:如果值相等,线程会将内存位置的值更新为新值。

返回:操作完成后,CAS会返回一个布尔值,表示操作是否成功。

如果内存位置的值在比较和交换之间被其他线程更改,那么CAS操作会失败,因为内存位置的值与预期原值不再匹配。

CAS 的核心 UnSafe

UnSafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问, UnSafe相当于一个后门,基于该类可以直接操作特定内存的数据。

Unsafe类中的所有方法都是Native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务。

CAS 的使用

下面是一个简单案例:

public class CASDemo {

public static void main(String[] args) {

// 创建一个 AtomicInteger 对象,并初始化为 5

AtomicInteger atomicInteger = new AtomicInteger(5);

// 使用 compareAndSet 方法尝试将 atomicInteger 的值从 5 改为 2020

// 如果当前值是 5,则修改成功,返回 true,并且 atomicInteger 的值变为 2020

// 否则,修改失败,返回 false,atomicInteger 的值保持不变

// 期望的结果是 true,因为当前值确实是 5

System.out.println(atomicInteger.compareAndSet(5, 2020) + "=>" + atomicInteger.get());

// 再次使用 compareAndSet 方法尝试将 atomicInteger 的值从 5 改为 1024

// 由于上一步操作已经将 atomicInteger 的值改为 2020,这次比较会失败

// 因此,返回 false,atomicInteger 的值保持为 2020

// 期望的结果是 false,因为当前值不再是 5

System.out.println(atomicInteger.compareAndSet(5, 1024) + "=>" + atomicInteger.get());

}

}

第一次compareAndSet调用成功,因为atomicInteger的值确实是5,所以值被更新为2020。

第二次compareAndSet调用失败,因为atomicInteger的值已经是2020,与预期值5不匹配,所以值保持不变,仍然是2020。

CAS 优缺点

优点

无锁并发:CAS操作不使用锁,因此不会导致线程阻塞,提高了系统的并发性和性能。

原子性:CAS操作是原子的,保证了线程安全。

缺点

ABA问题:CAS操作中,如果一个变量值从A变成B,又变回A,CAS无法检测到这种变化,可能导致错误。

自旋开销:CAS操作通常通过自旋实现,可能导致CPU资源浪费,尤其在高并发情况下。

单变量限制:CAS操作仅适用于单个变量的更新,不适用于涉及多个变量的复杂操作。

下面我们来详细讲一讲 ABA 问题。

什么是 ABA 问题

ABA 问题是指在并发编程中,当一个变量的值从A变为B,再从B变回A时,如果使用 CAS 操作,该操作可能会错误地认为变量的值没有改变,从而更新变量为另一个值,这可能导致数据不一致的问题。

ABA 问题示例:

import java.util.concurrent.atomic.AtomicInteger;

public class ABADemo {

private static AtomicInteger value = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {

Thread thread1 = new Thread(() -> {

value.set(1); // 线程1将值设置为1

value.set(0); // 线程1将值改回0

});

Thread thread2 = new Thread(() -> {

int originalValue = value.get(); // 读取值为0

while (originalValue == 0) {

if (value.compareAndSet(originalValue, 2)) { // 尝试将0改为2

System.out.println("Value changed to 2");

break;

}

originalValue = value.get(); // 重新读取值

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

}

}

本示例中,thread1将value从0改为1,然后又改回0。thread2尝试将value从0改为2。由于thread1的操作,thread2可能会错误地认为value仍然是0,并成功地将其改为2,即使value实际上已经经历了变化。

ABA 问题的解决

解决ABA问题的常见方法是使用带有版本号的原子引用类,如AtomicStampedReference。这个类通过维护一个“版本号”或“时间戳”来确保操作的原子性和正确性,类似乐观锁。

使用AtomicStampedReference解决ABA问题的代码示例:

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABASolutionDemo {

private static AtomicStampedReference value = new AtomicStampedReference<>(0, 0);

public static void main(String[] args) throws InterruptedException {

Thread thread1 = new Thread(() -> {

int[] stamp = new int[1];

value.getReference(stamp); // 获取当前值和版本号

value.set(1, stamp[0] + 1); // 线程1将值设置为1,并增加版本号

value.set(0, stamp[0] + 1); // 线程1将值改回0,并增加版本号

});

Thread thread2 = new Thread(() -> {

int[] stamp = new int[1];

while (value.getReference(stamp) == 0) {

if (value.compareAndSet(0, 2, stamp[0], stamp[0] + 1)) { // 尝试将0改为2,并增加版本号

System.out.println("Value changed to 2");

break;

}

stamp[0] = value.getStamp(); // 重新获取版本号

}

});

thread1.start();

thread2.start();

thread1.join();

thread2.join();

}

}

在这个解决方案中,AtomicStampedReference维护了一个值和一个版本号。每次更新值时,版本号都会增加,这样即使值被改回原来的值,版本号也已经改变,从而避免了ABA问题。compareAndSet方法现在需要检查值和版本号是否都匹配,只有两者都匹配时才会更新值和版本号。

相关推荐

什么都不想说的说说1
365足球体育亚洲版

什么都不想说的说说1

📅 07-21 👁️ 5602
十大正品保真购物软件
365bet网络娱乐

十大正品保真购物软件

📅 08-18 👁️ 5848
如何恢复qq同步数据库
365bet线上足球

如何恢复qq同步数据库

📅 07-16 👁️ 1621
北京前列腺医院哪家好「前三名」北京前列腺医院排行榜
365足球体育亚洲版

北京前列腺医院哪家好「前三名」北京前列腺医院排行榜

📅 08-06 👁️ 3770
快手电脑版如何下载 电脑怎么玩快手
365bet网络娱乐

快手电脑版如何下载 电脑怎么玩快手

📅 07-04 👁️ 866
《话筒如何接入主机并发出声音?——硬件连接和软件设置的详细教程》
苹果什么叫三码合一
365bet线上足球

苹果什么叫三码合一

📅 10-09 👁️ 9941
斗鱼TV怎么绑定手机号_斗鱼TV怎么解绑手机号
365bet网络娱乐

斗鱼TV怎么绑定手机号_斗鱼TV怎么解绑手机号

📅 08-19 👁️ 7305
赞美上帝的经典句子(合集50句)
365bet网络娱乐

赞美上帝的经典句子(合集50句)

📅 11-21 👁️ 1512