0

マップの初期化時に一度だけロックを作成したいのですが。これが私が使用しているコードです。

public static Map<String, String> getOrderStatusInstance() {
    if (orderStatusMap == null) {
        synchronized (Utility.class) {
            orderStatusMap = new HashMap<String, String>();

            orderStatusMap.put("Key1", "Value1");
            orderStatusMap.put("Key2", "Value2");
            orderStatusMap.put("Key3", "Value3");
            orderStatusMap.put("Key4", "Value4");
        }
    }

    return orderStatusMap;
}
4

4 に答える 4

9

いいえ、それは悪い考えです-変更可能でスレッドセーフではないマップを返します。また、ダブルチェックロックを実装しようとしていますが、適切に実装していません。静的初期化を使用するよりも、正しく実装するのが困難です。

理想的にはクラスの初期化の一部として、不変のマップを作成します(Guavaを優先的に使用します)。

private static final ImmutableMap<String, String> STATUS_MAP = ImmutableMap.of(
    "Key1", "Value1",
    "Key2", "Value2",
    "Key3", "Value3",
    "Key4", "Value4");

public static ImmutableMap<String, String> getOrderStatusInstance() {
    return STATUS_MAP;
}

(5つを超えるキー/値ペアの場合は、を使用しますImmutableMap.Builder。)

それよりも怠惰である必要が本当にありますか?

于 2012-05-21T17:50:51.633 に答える
2

ほぼ正しい...1つのスレッドがorderStatusMap==nullをチェックし、trueになる場合を考えてみてください。次に、スケジューラーは同じチェックを行う別のスレッドに切り替えます。両方のスレッドが同期ブロックを実行することはありません。

同期ブロックでnullをもう一度チェックすることで、これを防ぎます。

if (orderStatusMap == null) {
    synchronized (Utility.class) {
        if (orderStatusMap == null) {
            Map<String, String> tmp = new HashMap<String, String>();

            tmp.put("Key1", "Value1");
            tmp.put("Key2", "Value2");
            tmp.put("Key3", "Value3");
            tmp.put("Key4", "Value4");

            orderStatusMap = tmp;
        }
    }
}
return orderStatusMap;

はい、それを2回行うのは問題ありません。マップが作成された後、同期されたブロックに入るという高価なステップを実行する必要がなくなるため、外部チェックはパフォーマンスに役立ちます。

これはハッシュマップを作成するためのスレッドセーフな方法であることを忘れないでください。マップをスレッドセーフにするわけではありません。

PS:この質問が好きなら、あなたも好きかもしれません:HashMapを(文字通りの方法で)直接初期化する方法は?

于 2012-05-21T17:54:39.777 に答える
0

ブロックnull内をもう一度チェックする必要があります。synchronized

2つのスレッドがメソッドを呼び出すと、両方ともorderStatusMapnullを検出synchronizedし、一方はブロック内に入り、もう一方はブロックします。ただし、最終的には同期ブロック内に渡され、マップが再度初期化されます。

于 2012-05-21T17:50:37.407 に答える
0

いいえ、正しくありません

また、ここでは怠惰な初期化の必要はありません(とにかく使いすぎだと思います)ので、次のようにしてください。

private static final Map<String, String> orderStatusMap;
static{
    orderStatusMap = Collections.synchronizedMap(new HashMap<String, String>());//making it thread safe
    //or use a ConcurrentHashMap

    orderStatusMap.put("Key1", "Value1");
    orderStatusMap.put("Key2", "Value2");
    orderStatusMap.put("Key3", "Value3");
    orderStatusMap.put("Key4", "Value4");
}

public static Map<String, String> getOrderStatusInstance() {
    return orderStatusMap;
}
于 2012-05-21T18:06:14.237 に答える