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

线程安全自动填充引用:synchronized对比AtomicReference结合Soft/Weak Reference

线程安全的GC友好型延迟初始化方案:AtomicReference + Soft/WeakReference组合分析

针对你的需求——既要支持GC清理大对象,又要保证多线程环境下的安全性,同时避免锁阻塞和强引用内存泄漏——用AtomicReference持有SoftReference/WeakReference的组合是一个非常合适的方案,而且完全能满足你的线程安全和内存可见性要求,下面详细拆解:

核心结论先给你

这个组合能够提供与volatile+锁相当的线程安全级别,内存可见性有保障,同时完美规避了锁开销和强引用长期持有问题。

为什么内存可见性有保障?

你担心SoftReference本身没有volatile或同步机制,但关键点在于:我们不是直接操作SoftReference的内部字段,而是通过AtomicReference来持有SoftReference的实例。

AtomicReference的所有核心操作(get()、set()、compareAndSet())都是基于Unsafe类的volatile读写实现的——这意味着:

  • 当一个线程更新了AtomicReference中的SoftReference实例,其他线程调用get()时会立刻看到这个最新的引用,不存在内存可见性问题。
  • 即使SoftReference内部的referent字段没有volatile修饰,我们也不需要担心:因为我们只会在对象完全初始化完成后,才把对应的SoftReference放入AtomicReference,而后续读取时,要么拿到完整初始化的对象,要么因为GC回收拿到null(此时会触发重新初始化)。

线程安全性的具体实现(附代码示例)

针对你的延迟初始化场景,我们可以用AtomicReference的compareAndSet()实现无锁的线程安全初始化,避免重复创建对象的资源浪费:

// 针对重量级不可变对象的延迟初始化
private final AtomicReference<SoftReference<HeavyImmutableObject>> ref = new AtomicReference<>();

public HeavyImmutableObject get() {
    // 第一步:获取当前的SoftReference
    SoftReference<HeavyImmutableObject> softRef = ref.get();
    HeavyImmutableObject obj = (softRef != null) ? softRef.get() : null;
    
    // 如果对象已被GC或尚未初始化
    if (obj == null) {
        // 初始化新对象(注意:create方法要保证线程安全/幂等)
        HeavyImmutableObject newObj = createHeavyImmutableObject();
        SoftReference<HeavyImmutableObject> newSoftRef = new SoftReference<>(newObj);
        
        // CAS操作保证只有一个线程能成功设置新的引用
        if (!ref.compareAndSet(softRef, newSoftRef)) {
            // 其他线程已经完成初始化,取最新的引用
            obj = ref.get().get();
        } else {
            obj = newObj;
        }
    }
    return obj;
}

这个逻辑的线程安全保障点:

  1. compareAndSet()是原子操作,确保只有第一个完成初始化的线程能更新AtomicReference,避免重复创建大对象。
  2. AtomicReference.get()的volatile语义保证所有线程总能拿到最新的SoftReference实例,不会读取到过期的引用。
  3. 因为对象是不可变的,一旦初始化完成,后续读取不会有并发修改问题。

和volatile+锁方案的对比

  • 线程安全级别相当:两者都能保证内存可见性和初始化的原子性。
  • 性能更优:无锁的CAS操作避免了锁阻塞和上下文切换的开销,在高并发场景下优势明显。
  • 唯一的小区别:锁可以保证多步骤复合操作的原子性,但你的场景里只需要初始化的原子性,CAS完全足够覆盖。

注意事项

  1. 初始化方法的线程安全:如果createHeavyImmutableObject()不是幂等的(比如创建时会修改全局状态),可能会有多个线程同时执行该方法的情况(虽然只有一个能成功设置引用)。如果担心这个,可以给create方法加一个细粒度锁,或者用ThreadLocal做临时缓存,不过这种情况在不可变对象场景里很少见。
  2. 引用类型选择:根据业务场景选SoftReference(内存不足时回收)或WeakReference(无强引用时立刻回收)。
  3. 静态对象适配:静态对象的实现逻辑完全一致,只需要把AtomicReference声明为静态即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:52:05