单例对象的工厂:这个代码是线程安全的吗?

ane*_*est 6 java singleton factory atomic thread-safety

我有一个用于许多单例实现的通用接口.接口定义了可以抛出检查异常的初始化方法.

我需要一个工厂,它将按需返回缓存的单例实现,并想知道以下方法是否是线程安全的?

更新1:请不要建议任何第3部分图书馆,因为这可能需要获得法律许可,因为可能的许可问题:-)

更新2:此代码可能会在EJB环境中使用,因此最好不要生成其他线程或使用类似的东西.

interface Singleton
{
    void init() throws SingletonException;
}

public class SingletonFactory
{
    private static ConcurrentMap<String, AtomicReference<? extends Singleton>> CACHE =
        new ConcurrentHashMap<String, AtomicReference<? extends Singleton>>();

    public static <T extends Singleton> T getSingletonInstance(Class<T> clazz)
        throws SingletonException
    {
        String key = clazz.getName();
        if (CACHE.containsKey(key))
        {
            return readEventually(key);
        }

        AtomicReference<T> ref = new AtomicReference<T>(null);
        if (CACHE.putIfAbsent(key, ref) == null)
        {
            try
            {
                T instance = clazz.newInstance();
                instance.init();
                ref.set(instance); // ----- (1) -----
                return instance;
            }
            catch (Exception e)
            {
                throw new SingletonException(e);
            }
        }

        return readEventually(key);
    }

    @SuppressWarnings("unchecked")
    private static <T extends Singleton> T readEventually(String key)
    {
        T instance = null;
        AtomicReference<T> ref = (AtomicReference<T>) CACHE.get(key);
        do
        {
            instance = ref.get(); // ----- (2) -----
        }
        while (instance == null);
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不完全确定第(1)和(2)行.我知道引用的对象被声明为volatile字段AtomicReference,因此第(1)行所做的更改应该在第(2)行立即可见 - 但仍有一些疑问......

除此之外 - 我认为使用ConcurrentHashMap地址将新密钥放入缓存的原子性.

你们看到这种方法有什么顾虑吗?谢谢!

PS:我知道静态持有者类习惯用法 - 我不使用它(由于ExceptionInInitializerError在单例实例化中抛出的任何异常被包装)以及随后NoClassDefFoundError哪些不是我想要捕获的东西.相反,我想通过捕获它并优雅地处理它来利用专用检查异常的优势,而不是解析EIIR或NCDFE的堆栈跟踪.

Pau*_*ora 0

考虑使用 Guava 的CacheBuilder. 例如:

private static Cache<Class<? extends Singleton>, Singleton> singletons = CacheBuilder.newBuilder()
   .build(
       new CacheLoader<Class<? extends Singleton>, Singleton>() {
         public Singleton load(Class<? extends Singleton> key) throws SingletonException {
           try {
             Singleton singleton = key.newInstance();
             singleton.init();
             return singleton;
           }
           catch (SingletonException se) {
             throw se;
           }
           catch (Exception e) {
             throw new SingletonException(e);
           }
         }
       });

public static <T extends Singleton> T getSingletonInstance(Class<T> clazz) {
  return (T)singletons.get(clazz);
}
Run Code Online (Sandbox Code Playgroud)

注意:此示例未经测试和编译。

Guava 的底层Cache实现将为您处理所有缓存和并发逻辑。