导读:本期聚焦于小伙伴创作的《AtomicStampedReference怎么利用戳记变量解决高并发下的状态判断失真》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《AtomicStampedReference怎么利用戳记变量解决高并发下的状态判断失真》有用,将其分享出去将是对创作者最好的鼓励。

在高并发编程中,CAS(Compare And Swap)操作是很多无锁并发工具的核心实现,但CAS操作存在一个典型的ABA问题:如果一个变量的值从A被改成B,再被改回A,CAS操作无法感知到这个中间变化过程,就会误认为变量没有被修改过,进而导致状态判断失真。AtomicStampedReference通过引入戳记变量的设计,完美解决了这个问题。

AtomicStampedReference怎么利用戳记变量解决高并发下的状态判断失真

AtomicStampedReference的核心设计

AtomicStampedReference内部维护了一个Pair对象,这个对象同时存储了对象的引用和一个整型的戳记(stamp)。每次对引用进行修改时,都需要同时更新引用值和戳记值,戳记会随着每次修改自动递增,这样即使引用值回到了之前的状态,戳记值也会不同,从而让CAS操作能够识别出中间的变化。

它的核心构造方法如下:

// 构造方法,传入初始引用和初始戳记
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

核心方法说明

AtomicStampedReference提供了几个核心方法用于操作引用和戳记,具体说明如下:

方法名作用说明
getReference()获取当前的引用值
getStamp()获取当前的戳记值
get(int[] stampHolder)同时获取引用值和戳记值,戳记值会被存入传入的数组中
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)如果当前引用等于预期引用,且当前戳记等于预期戳记,就将引用更新为新引用,戳记更新为新戳记,返回是否更新成功
attemptStamp(V expectedReference, int newStamp)如果当前引用等于预期引用,就将戳记更新为新戳记,引用不变

实际使用案例

下面通过一个简单的示例演示AtomicStampedReference如何解决ABA问题导致的状态判断失真。假设我们有一个账户余额的场景,初始余额是100,戳记为0,线程1要将余额改成200,线程2在中间将余额改成150再改回100,我们看AtomicStampedReference如何识别这个变化。

import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicStampedReferenceDemo {
    // 初始引用为100,初始戳记为0
    private static AtomicStampedReference<Integer> balance = new AtomicStampedReference<>(100, 0);

    public static void main(String[] args) throws InterruptedException {
        // 线程1:预期将余额从100改成200
        Thread thread1 = new Thread(() -> {
            int[] stampHolder = new int[1];
            Integer currentRef = balance.get(stampHolder);
            int currentStamp = stampHolder[0];
            System.out.println("线程1获取初始余额:" + currentRef + ",初始戳记:" + currentStamp);
            try {
                // 模拟线程1执行耗时,让线程2先完成ABA操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 尝试更新,预期引用100,预期戳记0,新引用200,新戳记1
            boolean success = balance.compareAndSet(currentRef, 200, currentStamp, currentStamp + 1);
            System.out.println("线程1更新余额结果:" + success + ",当前余额:" + balance.getReference() + ",当前戳记:" + balance.getStamp());
        });

        // 线程2:执行ABA操作,100->150->100
        Thread thread2 = new Thread(() -> {
            int[] stampHolder = new int[1];
            // 第一次修改:100改成150,戳记从0改成1
            Integer currentRef = balance.get(stampHolder);
            balance.compareAndSet(currentRef, 150, stampHolder[0], stampHolder[0] + 1);
            System.out.println("线程2第一次修改后余额:" + balance.getReference() + ",戳记:" + balance.getStamp());

            // 第二次修改:150改回100,戳记从1改成2
            currentRef = balance.get(stampHolder);
            balance.compareAndSet(currentRef, 100, stampHolder[0], stampHolder[0] + 1);
            System.out.println("线程2第二次修改后余额:" + balance.getReference() + ",戳记:" + balance.getStamp());
        });

        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }
}

运行上述代码后,线程1的更新操作会失败,因为虽然余额回到了100,但是戳记已经从0变成了2,和线程1预期的戳记0不匹配,CAS操作会识别出中间的变化,避免了状态判断失真。

适用场景说明

AtomicStampedReference适合用在需要感知引用中间变化的高并发场景,比如版本控制、状态流转需要记录变更次数的场景。如果只需要判断引用是否被修改过,不需要关心修改次数,也可以使用AtomicMarkableReference,它用布尔类型的标记代替整型戳记,更轻量。需要注意的是,戳记变量需要合理设计递增逻辑,避免溢出问题,通常高并发场景下戳记递增的速度不会达到整型溢出的阈值,如有特殊场景可以考虑使用长整型存储戳记。

AtomicStampedReference戳记变量高并发状态判断失真CAS修改时间:2026-07-16 03:24:29

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