Java の新しいコレクション ライブラリでスレッド セーフを犠牲にする理由は、パフォーマンス以外にありましたか?
3 に答える
はい、別の理由があります。コレクションのすべてのメソッドを同期化すると、コレクションはスレッドセーフになりますが、
- より高速なソリューションがあります(同時コレクションなど)
- とにかく十分ではありません。なぜなら、チェックしてから行動する操作(要素が存在するかどうかをチェックし、存在しない場合は追加するなど)、またはとにかく外部同期を必要とする反復を実装する必要があるためです。
さらに、スレッド セーフはまったく犠牲にされませんでした。これは、ArrayList (たとえば) を同期コレクション プロキシでラップするだけで、デフォルトで同期される古い Vector と同じ同期保証が得られるためです。
List<String> list = Collections.synchronizedList(new ArrayList<String>());
したがって、同期が不要な場合 (99% のケース) は高速コレクションを、必要な場合は同期コレクションを、両方を最大限に活用できます。
パフォーマンスが主な理由です。
ただし、哲学的/設計上の重要な理由もあります。個々のコレクション クラスをスレッド セーフにするだけでは、完全なスレッド セーフを達成することはできません。
安全な並行コードは、通常、異なるレベルでの同期を必要とします。次に例を示します。
- より高いレベル - 2 つのコレクションを同時にロックして、一方から要素を削除して他方に追加できるようにします)
- 下位レベル - 並行データ テーブル内のアイテムの個々の行をロックする
したがって、ある意味では、コレクション クラスを同期させることはかなり恣意的な決定であり、多くの (ほとんどではないにしても) 状況には適していません。したがって、コレクションを安全でないものにして、同時実行戦略 (必要かどうか、適切なレベルの粒度、トランザクションへのアプローチなど) をユーザーが決定できるようにすることをお勧めします。
それとは別に、ロックやスレッド セーフについてまったく心配する必要がないように、Clojure によって開拓されたアプローチを採用し、不変の永続コレクション クラスを使用するという選択肢もあります。しかし、それには、状態へのアプローチをもう少し大規模に再考する必要があります....
パフォーマンスが十分でない理由は何ですか?
必須のスレッド「安全性」(ほとんどの場合、おそらく不完全です。たとえば、putIfAbsent がありませんでした) が削除されたため、必要のないときにコストを支払う必要はありませんが、それでも物事を作成するオプションがあります。そうするときは、自分で(そして選択した粒度で)スレッドセーフにします。
プログラマーが考える必要がなく、すべての状況で使用でき、確実に動作し、大きなオーバーヘッドを追加しない、自動スレッドセーフを組み込む方法がないことに気付いたと思います。
また、"さらに新しい" 同時実行 utils パッケージには、非常に特殊な目的に役立ついくつかの新しいスレッドセーフ コレクションの実装が含まれていることにも注意してください。繰り返しになりますが、さまざまなタスクにはさまざまなツールが必要であり、古いコレクション クラスは、ほとんどのアプリケーションにあまり適していない非常に貧弱な中間点を提供していました。