13

次のマルチトンが与えられます:

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];
    }
}

getInstance()メソッドの高価な同期やダブルチェックロックの論争なしに、どうすればスレッドセーフで怠惰な状態を保つことができますか?シングルトンの効果的な方法はここで言及されていますが、それはマルチトンには及ばないようです。

4

5 に答える 5

18

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

うーん、いいですね!


元の答え

これは、JCiP で説明されているように、Memoizer パターンに基づいて構築されたソリューションです。他の回答の 1 つと同様に ConcurrentHashMap を使用しますが、未使用のインスタンスの作成につながる可能性があるマルチトン インスタンスを直接保存する代わりに、マルチトンの作成につながる計算を保存します。その追加のレイヤーは、未使用のインスタンスの問題を解決します。

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();
    }
}
于 2013-08-09T14:50:08.330 に答える
6

これにより、マルチトンのスレッドセーフなストレージメカニズムが提供されます。唯一の欠点は、 putIfAbsent()呼び出しで使用されないマルチトンを作成できることです。可能性は小さいですが、存在します。もちろん、それが起こる可能性はほとんどありませんが、それでも害はありません。

プラス面として、事前割り当てや初期化は不要であり、事前定義されたサイズ制限もありません。

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;
}
于 2012-06-20T20:24:35.490 に答える
4

少なくとも異なるインスタンスを同時に取得できるようにするには、ロックの配列を使用できます。

private static final Multiton[] instances = new Multiton[...];
private static final Object[] locks = new Object[instances.length];

static {
    for (int i = 0; i < locks.length; i++) {
        locks[i] = new Object();
    }
}

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

public static Multiton getInstance(int which) {
    synchronized(locks[which]) {
        if(instances[which] == null) {
            instances[which] = new Multiton(...);
        }
        return instances[which];
    }
}
于 2012-06-20T19:43:40.917 に答える
3

Java 8 の出現と および ラムダのいくつかの改善により、 a (およびおそらく a でさえ) をConcurrentMapより整然とした方法で実装できるようになりました。MultitonSingleton

public class Multiton {
  // Map from the index to the item.
  private static final ConcurrentMap<Integer, Multiton> multitons = new ConcurrentHashMap<>();

  private Multiton() {
    // Possibly heavy construction.
  }

  // Get the instance associated with the specified key.
  public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
    // Already made?
    Multiton m = multitons.get(key);
    if (m == null) {
      // Put it in - only create if still necessary.
      m = multitons.computeIfAbsent(key, k -> new Multiton());
    }
    return m;
  }
}

不快に感じるgetInstanceかもしれませんが、次のようにさらに最小限に抑えることができるのではないかと思います。

// Get the instance associated with the specified key.
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
  // Put it in - only create if still necessary.
  return multitons.computeIfAbsent(key, k -> new Multiton());
}
于 2013-12-02T15:29:34.557 に答える
2

AtomicReferenceArrayを探しています。

public class Multiton {
  private static final AtomicReferenceArray<Multiton> instances = new AtomicReferenceArray<Multiton>(1000);

  private Multiton() {
  }

  public static Multiton getInstance(int which) {
    // One there already?
    Multiton it = instances.get(which);
    if (it == null) {
      // Lazy make.
      Multiton newIt = new Multiton();
      // Successful put?
      if ( instances.compareAndSet(which, null, newIt) ) {
        // Yes!
        it = newIt;
      } else {
        // One appeared as if by magic (another thread got there first).
        it = instances.get(which);
      }
    }

    return it;
  }
}
于 2012-06-21T01:18:52.570 に答える