如何理解CAS无锁算法与Atomic原子类在JUC的应用

来源:站长联盟作者:小鱼头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何理解CAS无锁算法与Atomic原子类在JUC的应用》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何理解CAS无锁算法与Atomic原子类在JUC的应用》有用,将其分享出去将是对创作者最好的鼓励。

在Java并发编程领域,JUC(java.util.concurrent)包是处理多线程协作的核心工具集,其中CAS无锁算法和Atomic原子类的组合,为开发者提供了不需要使用重量级锁就能实现线程安全操作的能力,在高并发场景下能显著提升程序性能。

CAS无锁算法基础

CAS全称为Compare And Swap,即比较并交换,是一种乐观锁的实现思路,核心逻辑是假设多线程操作共享变量时不会产生冲突,只有在更新数据的时候才会检查当前值是否被其他线程修改过。

CAS的执行流程

CAS操作包含三个核心参数:

  • V:需要更新的共享变量内存地址
  • E:期望的当前变量值
  • N:准备更新的新值

执行时,会先比较V地址对应的值是否等于E,如果相等,就把V地址的值更新为N,操作成功;如果不相等,说明有其他线程修改过该变量,当前操作失败,通常会进行重试或者放弃更新。

CAS的优缺点

优点在于不需要阻塞线程,避免了线程上下文切换的开销,在高并发、读多写少的场景下性能表现优异。缺点主要有三个:一是存在ABA问题,即变量从A被改成B又改回A,CAS检查时会认为没有变化;二是循环时间长开销大,如果一直更新失败会不断重试;三是只能保证单个变量的原子操作,多个变量需要配合其他机制实现原子性。

Atomic原子类概述

Atomic原子类是JUC包下基于CAS机制实现的线程安全类,所有操作都能保证原子性,不需要开发者手动加锁。根据操作的数据类型,Atomic原子类可以分为以下几类:

分类典型类适用场景
基本类型原子类AtomicInteger, AtomicLong, AtomicBoolean对int、long、boolean类型变量做原子操作
数组类型原子类AtomicIntegerArray, AtomicLongArray对数组中的元素做原子操作
引用类型原子类AtomicReference, AtomicStampedReference对对象引用做原子操作,后者可解决ABA问题
字段更新原子类AtomicIntegerFieldUpdater, AtomicLongFieldUpdater对对象的指定字段做原子操作,字段需要是volatile修饰的

Atomic原子类底层对CAS的应用

Atomic原子类的核心方法都依赖Unsafe类的CAS操作实现,以AtomicIntegerincrementAndGet方法为例,其底层逻辑就是不断尝试用CAS更新当前值,直到更新成功。

以下是模拟AtomicInteger自增逻辑的简化代码示例:

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class SimpleAtomicInteger {
    private volatile int value;
    private static final Unsafe unsafe;
    private static final long valueOffset;

    static {
        try {
            // 获取Unsafe实例
            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            unsafe = (Unsafe) unsafeField.get(null);
            // 获取value字段的内存偏移量
            valueOffset = unsafe.objectFieldOffset(SimpleAtomicInteger.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    public SimpleAtomicInteger(int initialValue) {
        this.value = initialValue;
    }

    // 自增并返回自增后的值,模拟AtomicInteger的incrementAndGet方法
    public final int incrementAndGet() {
        int current;
        do {
            // 获取当前value值作为期望值
            current = unsafe.getIntVolatile(this, valueOffset);
            // 期望值加1作为新值,尝试CAS更新
        } while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
        // 更新成功,返回新值
        return current + 1;
    }

    public int get() {
        return value;
    }
}

上述代码中,compareAndSwapInt就是Unsafe类提供的CAS本地方法,如果当前对象的valueOffset偏移量对应的值等于current,就更新为current + 1,否则循环重试,直到更新成功,这就是Atomic原子类无锁实现原子操作的核心逻辑。

典型应用场景示例

CAS和Atomic原子类非常适合计数器、状态标记、简单版本号控制等场景,以下是两个常见应用示例:

线程安全计数器

使用AtomicInteger实现高并发场景下的计数,不需要加锁就能保证计数准确:

import java.util.concurrent.atomic.AtomicInteger;

public class CounterDemo {
    private static final AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        // 创建10个线程,每个线程计数1000次
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.incrementAndGet();
                }
            });
            threads[i].start();
        }
        // 等待所有线程执行完成
        for (Thread thread : threads) {
            thread.join();
        }
        // 输出最终计数结果,应该是10000
        System.out.println("最终计数结果:" + counter.get());
    }
}

解决ABA问题的版本号控制

如果需要避免ABA问题,可以使用AtomicStampedReference,它在CAS的基础上增加了版本号,每次更新都会递增版本号,即使值被改回原值,版本号也不同,CAS会识别到变化:

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {
    public static void main(String[] args) {
        // 初始值100,初始版本号0
        AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(100, 0);

        Thread t1 = new Thread(() -> {
            int stamp = reference.getStamp();
            System.out.println("线程1初始版本号:" + stamp);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 尝试将100改成101,再改回100,版本号会递增
            reference.compareAndSet(100, 101, stamp, stamp + 1);
            reference.compareAndSet(101, 100, reference.getStamp(), reference.getStamp() + 1);
        });

        Thread t2 = new Thread(() -> {
            int stamp = reference.getStamp();
            System.out.println("线程2初始版本号:" + stamp);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程2用初始版本号尝试更新,此时版本号已经被线程1修改,更新失败
            boolean success = reference.compareAndSet(100, 200, stamp, stamp + 1);
            System.out.println("线程2更新是否成功:" + success);
            System.out.println("当前值:" + reference.getReference() + ",当前版本号:" + reference.getStamp());
        });

        t1.start();
        t2.start();
    }
}

上述代码中,线程1修改了两次值,版本号从0变成了2,线程2用初始版本号0尝试更新,会失败,这样就避免了ABA问题带来的逻辑错误。

使用注意事项

在实际使用CAS和Atomic原子类时,需要注意几个问题:一是如果竞争激烈,CAS重试次数过多会导致CPU开销增大,此时可以考虑使用锁或者其他并发方案;二是ABA问题是否会影响业务逻辑,如果会影响,需要选择带版本号的原子类;三是Atomic原子类只能保证单个操作的原子性,多个原子操作的组合需要额外保证原子性,比如先判断再更新多个变量,不能依赖单个Atomic类的操作。

CASAtomic原子类JUC无锁算法修改时间:2026-06-14 22:15:27

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。