java - 单例对象工厂 : is this code thread-safe?

标签 java thread-safety singleton factory atomic

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

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

UPDATE1:请不要建议任何第三部分库,因为由于可能的许可问题,这将需要获得法律许可:-)

更新 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;
    }
}

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

除此之外 - 我认为 ConcurrentHashMap 的使用解决了将新 key 放入缓存的原子性问题。

你们看到这种方法有什么问题吗?谢谢!

P.S.:我知道静态持有者类惯用语 - 由于 ExceptionInInitializerError(在单例实例化过程中抛出的任何异常都被包装到其中),我没有使用它,并且随后的 NoClassDefFoundError 这不是我想要捕捉的。相反,我想通过捕获并优雅地处理它来利用专用检查异常的优势,而不是解析 EIIR 或 NCDFE 的堆栈跟踪。

最佳答案

您已经做了很多工作来避免同步,我认为这样做的原因是出于性能考虑。您是否测试过与同步解决方案相比,这是否真的提高了性能?

我问的原因是并发类往往比非并发类慢,更不用说原子引用的附加重定向级别了。根据您的线程争用,简单的同步解决方案实际上可能更快(并且更容易验证正确性)。

此外,我认为在调用 instance.init() 期间抛出 SingletonException 时,您可能会以无限循环结束。原因是在 readEventually 中等待的并发线程永远不会找到它的实例(因为在另一个线程初始化实例时抛出异常)。也许这对您的情况来说是正确的行为,或者您可能想为实例设置一些特殊值以触发将异常抛出到等待线程。

关于java - 单例对象工厂 : is this code thread-safe?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8331413/

相关文章:

ios - 无法在单例 IOS 中设置长值

C++ 单例用法 : compiler complains about private constructor

Ruby 访问魔法

java - 无法加载驱动程序类 : org. h2.Driver with spring boot

java - 项目抛出多个编译错误异常

java - 当池中仍有线程运行时,为什么 executor.isShutdown() 返回 true?

c++ - 为什么 boost::shared_ptr 使用 gcc 内联汇编来增加 use_count 而不是使用 operator++?

java - 通过REST API处理ManyToOne实体集合的更新

java - 点击 Galaxy Samsung S3 上的菜单按钮时应用停止

java - 如何从Semaphore中获取获取到的索引