スレッドセーフです。ただし、スレッドセーフである方法は、期待したものとは異なる場合があります。あなたが見ることができるいくつかの「ヒント」があります:
Hashtable
このクラスは、スレッド セーフに依存しているが同期の詳細には依存していないプログラムと完全に相互運用可能です。
全体像をより完全に把握するには、ConcurrentMap
インターフェースを意識する必要があります。
オリジナルMap
は、いくつかの非常に基本的な読み取り/更新メソッドを提供します。私でさえ、Map
;のスレッドセーフな実装を作成できました。私の同期メカニズムを考慮しないと、人々が私のマップを使用できない場合がたくさんあります。これは典型的な例です:
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
マップ自体はスレッドセーフですが、このコードはスレッドセーフではありません。containsKey()
同時に呼び出す 2 つのスレッドは、そのようなキーがないと判断する可能性があるため、両方とも に挿入しMap
ます。
この問題を解決するには、追加の同期を明示的に行う必要があります。私のマップのスレッドセーフが同期されたキーワードによって達成されると仮定すると、次のことを行う必要があります。
synchronized(threadSafeMap) {
if (!threadSafeMap.containsKey(key)) {
threadSafeMap.put(key, value);
}
}
このような追加コードでは、マップの「同期の詳細」について知っておく必要があります。上記の例では、同期が「同期」によって達成されることを知る必要があります。
ConcurrentMap
インターフェイスはこれをさらに一歩進めます。マップへの複数のアクセスを含む、いくつかの一般的な「複雑な」アクションを定義します。たとえば、上記の例は として公開されputIfAbsent()
ます。これらの「複雑な」アクションにより、ConcurrentMap
(ほとんどの場合) のユーザーは、アクションをマップへの複数のアクセスと同期させる必要がなくなります。したがって、Map の実装では、パフォーマンスを向上させるために、より複雑な同期メカニズムを実行できます。 ConcurrentHashhMap
は良い例です。実際、スレッドセーフは、マップの異なるパーティションに対して個別のロックを維持することによって維持されます。マップへの同時アクセスによって内部データ構造が破損したり、更新が予期せず失われたりすることがないため、スレッドセーフです。
上記のすべてを念頭に置くと、Javadoc の意味がより明確になります。
ConcurrentHashMap
スレッドセーフのために「同期」を使用していないため、「取得操作(取得を含む)は通常ブロックされません」 。それ自体のロジックがget
スレッドセーフを処理します。さらに Javadoc を調べると、次のようになります。
テーブルは内部的に分割され、指定された数の同時更新を競合なしで許可しようとします
検索がノンブロッキングであるだけでなく、更新も同時に発生する可能性があります。ただし、非ブロッキング/同時更新は、スレッドが安全でないことを意味するものではありません。これは単に、スレッドセーフのために単純な「同期」以外の方法を使用していることを意味します。
ただし、内部の同期メカニズムは公開されていないため、 が提供するもの以外の複雑なアクションを実行するConcurrentMap
場合は、ロジックの変更を検討するか、 を使用しないことを検討する必要がありますConcurrentHashMap
。例えば:
// only remove if both key1 and key2 exists
if (map.containsKey(key1) && map.containsKey(key2)) {
map.remove(key1);
map.remove(key2);
}