ConcurrentHashMapという選択
最近「java.util.HashMap」がスレッドセーフじゃないことを知って
それについていろいろ調べてみた。
まず、「java.util.HashMap」はスレッドセーフじゃないから、複数スレッドが並行してHashMapにアクセスする場合はハッシュテーブルの破壊とか無限ループ、メモリリークを起きてしまうことがあるみたい。
なんで、その対策としては基本的にはHashMapの代わりに以下のようなsynchronizedMapを使うのがいいらしい。
Map map = Collections.synchronizedMap(new HashMap(...));
ただ、このsynchronizedMapについても少し調べてみると、実はこれはこれで問題があるらしく、
Iteratorで要素を取り出すときに同期を保証してくれないから、Mapの要素数が変わったりするとConcurrentModificationExceptionが発生してしまうみたい。
なんでsynchronizedMapを使う場合は結局自分でMapとかをsynchronizedしないといけないことになる。
Mapをsynchronizedしたもの
Map map = Collections.synchronizedMap(new HashMap(...)); synchronized(map){ Set entries = map.entrySet(); Iterator it = entries.iterator(); while(it.hasNext()){ } }
でも実は上記の無限ループとかIterater問題を解決する為にJDK1.5からConcurrentHashMapっていうクラスが追加されていたらしく、これが基本的にHashMapと同じ機能を持ちながら上記の問題を解決してくれるらしい。
しかもパフォーマンスもsynchronizedした時より全然いいみたい。
宣言の仕方とかはHashMapとかとまったく同じ。
ConcurrentHashMap map = new ConcurrentHashMap();
なんで、結論としては複数スレッドからアクセスがある状況でMapを使う場合は、基本ConcurrentHashMapを使った実装方法を選択すること。
※既存のソースでHashMapからConcurrentHashMapに移行したい場合、nullの扱いが違うことからそれなりに修正はしないといけない。
参考:http://software.fujitsu.com/jp/technical/interstage/apserver/guide/pdf/concurrenthashmap-20120105.pdf
2013/12/9追記:ただし、実装方法によっては必ずしも同期化されるわけではないので、下記のような実装は考慮する必要あり。
http://software.fujitsu.com/jp/technical/interstage/apserver/guide/pdf/ConcurrentMap.pdf