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

是否可使用AtomicReference实现支持测试替换的懒加载单例模式?

Absolutely, AtomicReference is a perfect choice for building a lazy-loaded, thread-safe singleton that you can easily swap out during testing—no messy double-checked locking required. Let’s walk through how this works and why it solves your exact needs.

Why AtomicReference Fits Your Use Case

It addresses all your key requirements: lazy loading, thread safety without double-checked locking, and straightforward testability. Here’s a concrete implementation to illustrate:

import java.util.concurrent.atomic.AtomicReference;

public class ExamSingleton {
    // AtomicReference holds our singleton instance
    private static final AtomicReference<ExamInterface> instance = new AtomicReference<>();

    // Lazy-load the default instance when first requested
    public static ExamInterface getInstance() {
        // Fast path: return existing instance if it exists
        if (instance.get() == null) {
            // Atomic CAS operation ensures only one thread initializes the instance
            instance.compareAndSet(null, new SimpleExam());
        }
        return instance.get();
    }

    // Expose a setter to replace the instance for testing scenarios
    public static void setInstance(ExamInterface customInstance) {
        instance.set(customInstance);
    }

    // Optional: Reset instance to clean up state between tests
    public static void resetInstance() {
        instance.set(null);
    }
}

// Abstract interface to enable easy substitution
interface ExamInterface {
    void executeExam();
}

// Default production implementation
class SimpleExam implements ExamInterface {
    @Override
    public void executeExam() {
        // Your default exam logic here
    }
}

Key Advantages of This Approach

  • Lazy loading: The SimpleExam instance is only created on the first call to getInstance()—no upfront initialization, which aligns with your preference.
  • Thread safety: The compareAndSet method is atomic, so even if multiple threads hit the initialization check simultaneously, only one will successfully create the instance. Others will immediately receive the already-initialized value once it’s set. No need for synchronized blocks or volatile variables (though those work post-JDK 1.5, this implementation is cleaner and harder to mess up).
  • Test-friendly substitution: Instead of relying on reflection or hacks, you can call ExamSingleton.setInstance(new MockExam()) in your test setup to replace the default implementation. Use resetInstance() after tests to avoid cross-test state contamination.
  • Avoids DCL complexity: Even though double-checked locking is fixed in modern JDKs, it’s easy to make mistakes (like forgetting the volatile keyword). AtomicReference makes thread-safe initialization explicit and intuitive.

Quick Note for Complex Initialization

If your SimpleExam initialization is expensive or has side effects, keep in mind that multiple threads might enter the if (instance.get() == null) block before the CAS succeeds. This means a few threads might run the initialization code, but only the first result will be stored. If this is a concern, you could wrap initialization in a supplier or add a separate atomic flag to guard the process—but for most use cases, the basic implementation above works perfectly.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:10:00