You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AtomicReference的getAndUpdate方法原子性实现与使用疑问

问题1解答

  • 多线程调用示例2的代码时,其他线程必然可能观测到被修改的中间状态,getAndUpdate不会对传入的原始值做任何克隆操作,这个写法本身就是错误的,无法保证原子性。
  • 原理说明:AtomicReference的getAndUpdate方法仅保证「读取当前持有的引用、调用更新函数生成新引用、CAS原子替换旧引用为新引用」这一系列操作中,最终的引用替换是原子的,它的原子性保障仅作用于引用本身,完全不会干预你对引用指向的实际对象的操作。你在回调里拿到的current就是AtomicReference当前存储的引用指向的真实HashSet实例,直接修改这个实例的行为没有任何同步保障,多线程同时操作时会触发HashSet的并发问题:比如元素丢失、抛出ConcurrentModificationException、出现异常的中间状态都有可能。
  • 至于为什么允许这么写:AtomicReference是通用的泛型工具,它只负责引用层面的原子操作,既没有能力也没有义务去限制使用者对引用指向的对象做什么操作,Java语法层面也没法禁止你在lambda中修改传入的对象参数,这种问题属于用法错误,不在工具的限制范围内。

问题2解答

二者的核心区别在于更新逻辑的安全性:

  • 直接调用set的写法是无脑覆盖,完全不管当前AtomicReference里存储的值有没有被其他线程修改过。举个例子:如果有两个线程同时执行更新逻辑,线程A要加元素test1,线程B要加元素test2,用直接set的写法时,两个线程都会先拿到初始的空Set,各自新建Set添加完自己的元素之后调用set,最终只会有一个线程的修改生效,另一个的修改会被直接覆盖,最终Set里只会有一个元素。
  • 示例1的getAndUpdate写法是基于CAS的乐观锁实现,CAS替换失败时会自动重试回调函数,直到替换成功。*注意你给出的示例1代码里是直接新建空Set添加元素,没有复用当前的current值,这种特殊写法和直接set效果没区别,但如果是标准写法,先拷贝current的元素到新Set再做修改:
set.getAndUpdate(current -> {
    Set<String> updated = new HashSet<>(current);
    updated.add("test");
    return updated;
});

这种写法就能保证你的修改一定是基于当前最新的Set值做的,不会丢失其他线程的更新,刚才的双线程更新场景下最终Set会同时包含test1和test2。


内容的提问来源于stack exchange,提问作者user3908406

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 14:06:02