Javaで書かれた人のコードをみていてConcurrentHashMapを使っているのを見つけた
HashMapの同期化には問題があるのは頭の片隅にはあったが、Javaを普段書かないので、なにが問題なのか、ConcurrentHashMapとの違いが自分の中で説明ができなかったので調べることにした
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を使うと覚えておくでよさそう