これは私も答えを探していた問題です。このメソッドputIfAbsent
は、余分なオブジェクト作成の問題を実際に解決するわけではなく、これらのオブジェクトの 1 つが別のオブジェクトを置き換えないようにするだけです。ただし、スレッド間の競合状態により、複数のオブジェクトがインスタンス化される可能性があります。この問題に対する 3 つの解決策を見つけることができました (そして、この優先順位に従います)。
computeIfAbsent
1- Java 8 を使用している場合、これを実現する最善の方法は、おそらくConcurrentMap
. 同期的に実行される計算関数を与えるだけです(少なくともConcurrentHashMap
実装のために)。例:
private final ConcurrentMap<String, List<String>> entries =
new ConcurrentHashMap<String, List<String>>();
public void method1(String key, String value) {
entries.computeIfAbsent(key, s -> new ArrayList<String>())
.add(value);
}
これは、次の javadoc からのものですConcurrentHashMap.computeIfAbsent
。
指定されたキーがまだ値に関連付けられていない場合、指定されたマッピング関数を使用してその値を計算し、null でない限りこのマップに入力しようとします。メソッド呼び出し全体がアトミックに実行されるため、関数はキーごとに最大 1 回適用されます。他のスレッドがこのマップに対して試行した更新操作の一部は、計算の進行中にブロックされる可能性があるため、計算は短く単純にする必要があり、このマップの他のマッピングを更新しようとしてはなりません。
2- Java 8 を使用できない場合は、スレッドセーフなGuava
'sを使用できます。LoadingCache
ロード関数を (compute
上記の関数と同様に) 定義すると、確実に同期的に呼び出されます。例:
private final LoadingCache<String, List<String>> entries = CacheBuilder.newBuilder()
.build(new CacheLoader<String, List<String>>() {
@Override
public List<String> load(String s) throws Exception {
return new ArrayList<String>();
}
});
public void method2(String key, String value) {
entries.getUnchecked(key).add(value);
}
3- Guava も使用できない場合は、いつでも手動で同期し、ダブルチェック ロックを実行できます。例:
private final ConcurrentMap<String, List<String>> entries =
new ConcurrentHashMap<String, List<String>>();
public void method3(String key, String value) {
List<String> existing = entries.get(key);
if (existing != null) {
existing.add(value);
} else {
synchronized (entries) {
List<String> existingSynchronized = entries.get(key);
if (existingSynchronized != null) {
existingSynchronized.add(value);
} else {
List<String> newList = new ArrayList<>();
newList.add(value);
entries.put(key, newList);
}
}
}
}
これら 3 つのメソッドすべての実装例を作成し、さらに非同期メソッドを作成しました。これにより、余分なオブジェクトが作成されます: http://pastebin.com/qZ4DUjTr