1

(おそらく、「Javaでジェネリックシングルトンクラスを作成する方法」に対する補足的な質問です:)

class MyClass<T> {
    private static Map<Class<MyClass<?>>, MyClass<?>> s_instances =
        new HashMap<Class<MyClass<?>>, MyClass<?>>();

    public static MyClass<?> blah(Class<MyClass<?>> clz)
            throws InstantiationException, IllegalAccessException {
        if (s_instances.get(clz) != null)
            return s_instances.get(clz);
        MyClass<?> instance = clz.newInstance();
        s_instances.put(clz, instance);
        return instance;
    }
}

タイプごとにシングルトンの引数値を持つためのより良いイディオムはありますか?

編集:スレッドセーフの欠如を指摘するためだけに答えないでください。取ったポイント。この地図よりももっとエレガントなことができるかどうか尋ねています。

4

2 に答える 2

1

あなたのメソッドはスレッドセーフではありません:

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances =
    new HashMap<Class<MyClass<?>>, MyClass<?>>();

public static MyClass<?> blah(Class<MyClass<?>> clz)
        throws InstantiationException, IllegalAccessException {
    if (s_instances.get(clz) != null)
        return s_instances.get(clz);
    // here1
    MyClass<?> instance = clz.newInstance();
    s_instances.put(clz, instance);
    // here2
    return instance;
}

1つのスレッドがマークされた行を通過すると//here1、最初のスレッドがマークされた行に到達する前に2番目のスレッドがメソッドに入る可能性があります//here2。したがって、同じ種類の2番目の「シングルトン」が作成され、マップの最初のスレッドが上書きされます。

手っ取り早い解決策は、マップ上で同期することです。

public static MyClass<?> blah(Class<MyClass<?>> clz)
        throws InstantiationException, IllegalAccessException {
  synchronized(s_instances){
    if (s_instances.get(clz) != null)
        return s_instances.get(clz);
    // here1
    MyClass<?> instance = clz.newInstance();
    s_instances.put(clz, instance);
    // here2
    return instance;
  }
}

ただし、これは、多くのスレッドが多くの時間を待たなければならず、最終的にはアプリケーションを強制終了することを意味します。おそらくあなたがすべきことは2段階の解決策です:

public static MyClass<?> blah(Class<MyClass<?>> clz)
        throws InstantiationException, IllegalAccessException {
  Object candidate = s_instances.get(clz);
  if(clz.isInstance(candidate)){ // implicit null check
      return clz.cast(candidate);
  }
  synchronized(s_instances){
    Object candidate = s_instances.get(clz);
    if(clz.isInstance(candidate)){  // gotta check a second time in a
        return clz.cast(candidate); // synchronized context
    }
    MyClass<?> instance = clz.newInstance();
    s_instances.put(clz, instance);
    return instance;
  }
}

また、HashMapは同時アクセスには適していないため、次のようにラップする必要がありますCollections.synchronizedMap()

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances =
    Collections.synchronizedMap(new HashMap<Class<MyClass<?>>, MyClass<?>>());

またはConcurrentHashMap代わりに行きます。

于 2012-11-19T10:28:02.940 に答える
1

お願い、それはやめて。
A. シングルトンはスレッド セーフではありません。
B. Java でのダブル チェック パターンの問題に注意してください。
C.各クラスに静的初期化子を持ち、次のものを持つのは本当に難しいですか。

static {
   instance = new MySingleton();
}

その後

public static MySingleton getInstance() {
return instance
}

そして、あなたが本当に主張するなら
-1.インスタンスへのマップ内のタイプを管理するシングルトンをおそらく定義できます(キーはクラスまたは完全なクラス名、値はオブジェクトです)
2.そこに目的のタイプを追加登録できます(お勧めします彼らにはプライベートCTORがあります)。
3.この回答を使用してプライベート CTOR を呼び出し、マップ エントリの値に配置されるインスタンスを作成します。
4. 1 で述べたリポジトリに getInstance メソッドを提供します。署名は次のとおりです。

public Object getInstanceByType(Class<?> clazz)

このメソッドは、内部マップからインスタンスを取得します。

于 2012-11-19T10:23:59.127 に答える