Java中的线程安全多圈

don*_*ton 11 java singleton multithreading thread-safety multiton

鉴于以下multiton:

public class Multiton 
{
    private static final Multiton[] instances = new Multiton[...];

    private Multiton(...) 
    {
        //...
    }

    public static Multiton getInstance(int which) 
    {
        if(instances[which] == null) 
        {
            instances[which] = new Multiton(...);
        }

        return instances[which];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果没有getInstance()方法的昂贵同步和双重检查锁定的争议,我们如何保持线程安全和懒惰?这里提到了单身人士的有效方法,但似乎并没有扩展到多人.

ass*_*ias 15

更新:使用Java 8,它可以更简单:

public class Multiton {
    private static final ConcurrentMap<String, Multiton> multitons = new ConcurrentHashMap<>();

    private final String key;
    private Multiton(String key) { this.key = key; }

    public static Multiton getInstance(final String key) {
        return multitons.computeIfAbsent(key, Multiton::new);
    }
}
Run Code Online (Sandbox Code Playgroud)

嗯那很好!


原始答案

这是一个基于JCiP中描述的Memoizer模式的解决方案.它使用ConcurrentHashMap,就像其他答案之一一样,但它不是直接存储Multiton实例,而是可以导致创建未使用的实例,而是存储导致创建Multiton的计算.该附加层解决了未使用实例的问题.

public class Multiton {

    private static final ConcurrentMap<Integer, Future<Multiton>> multitons = new ConcurrentHashMap<>();
    private static final Callable<Multiton> creator = new Callable<Multiton>() {
        public Multiton call() { return new Multiton(); }
    };

    private Multiton(Strnig key) {}

    public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
        Future<Multiton> f = multitons.get(key);
        if (f == null) {
            FutureTask<Multiton> ft = new FutureTask<>(creator);
            f = multitons.putIfAbsent(key, ft);
            if (f == null) {
                f = ft;
                ft.run();
            }
        }
        return f.get();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果两个线程调用run(),FutureTask的行为是什么?我假设任务只运行一次,但javadocs在这一点上并不是100%清楚.编辑:我现在意识到这不会发生,因为run()只能由成功放入()FutureTask的线程调用. (2认同)

Rob*_*bin 5

这将为您的Multitons提供线程安全存储机制.唯一的缺点是可以创建一个不会在putIfAbsent()调用中使用的Multiton.可能性很小,但确实存在.当然,它发生的可能性很小,但仍然没有造成任何伤害.

从好的方面来说,不需要预分配或初始化,也没有预定义的大小限制.

private static ConcurrentHashMap<Integer, Multiton> instances = new ConcurrentHashMap<Integer, Multiton>();

public static Multiton getInstance(int which) 
{
    Multiton result = instances.get(which);

    if (result == null) 
    {
        Multiton m = new Multiton(...);
        result = instances.putIfAbsent(which, m);

        if (result == null)
            result = m;
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)