技術開発日記

技術やら日々思ったことを綴ってます。

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