UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Java】HashMapとConcurrentHashMap

Javaで書かれた人のコードをみていてConcurrentHashMapを使っているのを見つけた

HashMapの同期化には問題があるのは頭の片隅にはあったが、Javaを普段書かないので、なにが問題なのか、ConcurrentHashMapとの違いが自分の中で説明ができなかったので調べることにした

docs.oracle.com

HashMapクラスの同期化には問題がある

API 仕様に明記されているように、HashMap クラスは同期化されない

そのため、複数のスレッドが並行してHashMap にアクセスし、そのうちのいずれかのスレッドでマップを変更する場合は外部で同期をとる必要がある

例えば、マップにあるかを確認して、なければそのマップにputするような処理など

これらの処理で適切な実装しない場合、ハッシュテーブルの破壊や無限ループ、メモリリークといった問題を引き起こすことがある

コンパイル時ではわからないし、ピンポイントなテストをしない限り絶対に気づかないので本当によく引き起こしてしまうらしい

ConcurrentHashMap

HashMapの同期化には問題があるので、内部的に同期化をする仕組みが必要がある

しかし、単にハッシュテーブルにアクセスするたびに同期化をする(Hashtableクラスを使うなどする)と性能が著しく悪化する

そこで、Java5で導入されたConcurrentHashMapは、基本的に複数のスレッドが同時にアクセスできるようして性能が悪くならないようにするが、テーブルが破壊されるような場合には自動的に同期化するように実装されている

nullの扱い

単にHashMapをConcurrentHashMapに置き換える場合に注意点があった

HashMapはエントリーのキーや値に null を使用できるが、ConcurrentHashMap では使用できない

ConcurrentHashMap に対して値が null のエントリーを登録しようとすると NullPointerException が発生するので、単にHashMapからConcurrentHashMapに置き換えると、予期せぬ例外が発生する可能性がある

computeIfAbsent

ConcurrentHashMapを使った場合でも単に自前でマップになければ値をputする処理を実装するとテーブルが破壊されることがあったらしく、Java8からcomputeIfAbsentというメソッドが実装された

どう実装しているかは難しかったので、マップにない場合はputするような処理がある場合は脳死でConcurrentHashMap.computeIfAbsentを使うと覚えておくでよさそう

他参考

https://www.fujitsu.com/jp/documents/products/software/resources/technical/interstage/apserver/guide/concurrenthashmap-20120105.pdf