ConcurrentHashMapを使うならatomicなメソッドを使おう
ConcurrentHashMapを使うときの注意点
タイトルの通りです。
ConcurrentHashMapは同期を取りつつ、パフォーマンスも優れたとても優秀なやつで、最近Webアプリケーションで、キャッシュみたいなものを作るときに使いました。
とはいえ同期を取るとはいっても以下のようなキーの存在を確認して、putするような複合アクションの呼び出し(いわゆるcheck-then-act)では同期されません。(別にこれは、従来のHahMapとかCollections.syncronizedMapとかでも同じです)
// atomicではない操作。 if(!map.containsKey(key)) { map.put(key, value); }
以下が実証コードで、実行すると高い確率で複数スレッドで同一キーへのputを行います。
(今回のケースだとあまり問題にならないですが、valueを更新するような操作だと更新内容が喪失する可能性はあります。
また同期化されない処理は後々厄介なバグを生んだりするそうです。参考資料より。)
Atomicな操作
で、こんな時のためにConcurrentHashMapには、キーがなければ値を追加するという上記のような操作を行うputIfAbsentメソッドがあります。
// 上とほぼ同等な内容だが、atomicな操作。
map.putIfAbsent(key, value)
atomicなメソッドはほかに、replace, removeなんかがあります。 というわけで、もし既存のソースでMapをConcurrentHashMapに置き換える時は、上記のようは複合アクションを行っていないかも確認した方が良いという話でした。
参考資料
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―
- 作者: Brian Goetz,Joshua Bloch,Doug Lea
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/11/22
- メディア: 単行本
- 購入: 30人 クリック: 442回
- この商品を含むブログ (170件) を見る