13

次のイディオムを持つレガシーコードを見ています:

Map<String, Boolean> myMap = someGlobalInstance.getMap();
synchronized (myMap) {
    item = myMap.get(myKey);
}

Intelli-J のコード インスペクションから得られる警告は次のとおりです。

Synchronization on local variable 'myMap'

これは適切な同期ですか? またその理由は?

Map<String, Boolean> myMap = someGlobalInstance.getMap();
synchronized (someGlobalInstance.getMap()) {
    item = myMap.get(myKey);
}
4

4 に答える 4

12

これが問題としてフラグ付けされている理由は、ローカル変数で同期することは通常悪い考えであるためです。

によって返されるオブジェクトが常に同じである場合someGlobalInstance.getMap()、同期ブロックは実際にその準グローバル オブジェクト モニターを使用し、コードは期待される結果を生成します。

get()/put()呼び出しのみを同期する必要があり、より大きな同期ブロックがない場合は、同期ラッパーを使用するという提案にも同意します。ただし、Map はラッパーを介してのみアクセスするようにしてください。そうしないと、バグが発生する可能性がもう 1 つあります。

また、 が常に同じオブジェクトを返さない場合、2 番目のコード例でも正しく動作しないことに注意してsomeGlobalInstance.getMap()ください呼び出したオブジェクトとは異なるオブジェクトで同期できるため、元のコードよりもさらに悪い結果になる可能性がありますget()

于 2009-11-03T22:57:40.280 に答える
4

getMap() メソッドが何をするかによって、コードは健全になると思います。スレッド間で共有する必要があるインスタンスへの参照を保持している場合、それは理にかなっています。ローカル変数はローカルで初期化されていないため、警告は無関係です。

于 2009-11-03T22:55:23.657 に答える
2

Collections.synchronizedMap(Map)ここでは、呼び出しによる同期ラッパーの追加が典型的なアプローチであるという点で、Alex は正しいです。ただし、このアプローチを採用した場合でも、Mapのロックで同期する必要がある場合があります。たとえば、マップを反復するとき。

Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<String, String>());

// Synchronized on map to prevent ConcurrentModificationException whilst iterating.
synchronized (syncMap) {
  for (Map.Entry<String, String> entry : syncMap.entrySet()) {
    // Do work
  }
}

あなたの例では、IDEA からの警告は無視できます。ローカル変数:がメソッド内で作成されるのではなく、map別の場所 ( ) から取得されるため、他のスレッドからアクセスできる可能性があることが明らかです。someGlobalInstance

于 2009-11-03T23:03:16.350 に答える
2

マップには同期ラッパーを使用する方が良いと思います

于 2009-11-03T22:50:53.227 に答える