java并发线程深入理解CAS以及ABA问题的处理

本文详细介绍了Java并发中的CAS(Compare And Swap)操作,包括其实现过程、应用、并发锁的实现方式(如synchronized和ReentrantLock),以及如何通过Unsafe类使用CAS。文章还分析了CAS的源码,探讨了其无锁算法的特性。此外,文章着重讨论了CAS存在的ABA问题,并提出了AtomicStampedReference和AtomicMarkableReference两种解决方案,通过版本号确保数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是CAS

CAS(Compare And Swap,比较并交换),通常指的是这样一种原子操作:针对一个变量,首先比较它的内存值与某个期望值是否相同,如果相同,就给它赋一个新值。

CAS实现过程如下图:

1、一个初始值变量V,值为5;一开始先读取V实际内存中的值赋值给E 2、比如我们需要给最原始的V+1操作,那么此时用E+1来进行操作(这是防止V在其他线程已经被改变),这样完成了U=E+1的操作 3、判断E和V的值是否一致,如果一致则证明在以上操作过程中V没有被其他线程改变则将U的值赋值给V,如果不一致那V就被其他改变了,这样给U的+1操作就不成立,返回当前的V。

​CAS 的逻辑用伪代码描述如下:

if (value == expectedValue) {     
    value = newValue;

以上伪代码描述了一个由比较和赋值两阶段组成的复合操作,CAS 可以看作是它们合并后的整体——一个不可分割的原子操作,并且其原子性是直接在硬件层面得到保障的。

CAS可以看做是乐观锁(对比数据库的悲观、乐观锁)的一种实现方式,Java原子类中的递增操作就通过CAS自旋实现的。

CAS是一种无锁算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。

1-2、CAS应用

在java并发线程中,我们可以使用以下方式进行加锁处理:\

1、synchronize 在并发竞争比较激烈的时候不推荐使用,会让未获得锁的线程阻塞,因为会切换到内核态,进行park,这样就会有性能问题 2、使用ReentrantLock lock.lock(加锁) lock.unlock(解锁),需要注意的是unlock需要放到finally中,防止代码块异常,抛出后锁一直存在。 3、CAS也可以实现线程锁机制。

在 Java 中,CAS 操作是由 Unsafe 类提供支持的,该类定义了三种针对不同类型变量的 CAS 操作,如图

​它们都是 native 方法,由 Java 虚拟机提供具体实现,这意味着不同的 Java 虚拟机对它们的实现可能会略有不同。

以 compareAndSwapInt 为例,Unsafe 的 compareAndSwapInt 方法接收 4 个参数,分别是:对象实例、内存偏移量、字段期望值、字段新值。该方法会针对指定对象实例中的相应偏移量的字段执行 CAS 操作。

1-3、并发锁的实现

1-3-1、使用synchronized

我们都知道用synchronized可以实现锁机制,但是在并发量大的时候,会**锁竞争的现象,这样就会涉及到用户态到内核态的切换**,这是极其耗费性能的,因此建议在并发量不大的情况下,可以使用synchronized进行加锁。并发量大的时候,不建议使用

public class ThreadLockSynchronized {
    private volatile static int count = 0;

    static Object object = "";

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (object) {
                        for (int j = 0; j < 10000; j++) {
                            count++;
                        }
                    }
                }
            });
            thread.start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("count:" + count);
    }
}

以上代码测试结果如下:

1-3-2、使用ReentrantLock

使用ReentrantLock的时候一定要注意,要将unlock()放到finally代码块中,防止业务代码异常,无法释放锁

public class ThreadLockReentrantLock {
    private volatile static int count = 0;

    static ReentrantLock reentrantLock=new ReentrantLock();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //使用reentrantLock.lock();进行加锁操作
                    reentrantLock.lock();
                    try {
                        for (int j = 0; j < 10000; j++) {
                            count++;
                        }
                    } finally {
                        //reentrantLock.unlock();一定要放到finally中,防止业务代码异常,导致锁不释放
                        reentrantLock.unlock();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值