14 並行処理

Java SEの並行処理APIは、スレッド・プールやブロック・キューなど、パフォーマンスの高いスレッド・ユーティリティのための強力で拡張可能なフレームワークを提供します。このパッケージを利用するプログラマは、これらのユーティリティを自分で作成する手間から解放されます。ちょうど、Collections Frameworkのおかげでデータ構造を構築する労力が軽減されたのと同じです。さらに、これらのパッケージは、高度な並行処理プログラミングに利用できる下位レベルのプリミティブ機能も提供します。

パッケージjava.util.concurrentに含まれる並行処理APIは、並行処理クラスまたはアプリケーションの構成要素として使用するために設計されたクラスです。Collections Frameworkが、一般的に使用されるデータ構造の実装を提供することにより、インメモリー・データの編成と操作を簡素化したのと同様に、並行処理ユーティリティは、並行処理設計で一般的に使用される構築ブロックの実装を提供することにより、並行処理クラスの開発を簡素化します。並行処理ユーティリティには、高性能で柔軟なスレッド・プール、タスクの非同期な実行に対するフレームワーク、同時アクセスに対して最適化されたコレクション・クラスのホスト、計数セマフォ、原子変数、ロック、および条件変数などの同期ユーティリティが含まれています。

スレッド・プールなどのコンポーネントを独自開発する代わりに並行処理ユーティリティを使用すると、次のような利点があります。

  • プログラミングの労力の軽減。標準クラスを使用すれば、自分で開発するより簡単です。
  • パフォーマンスの向上。並行処理ユーティリティの実装は、並行処理およびパフォーマンスのエキスパートにより開発され精査されました。これらの実装はスキルを持つ開発者が行う標準的な実装よりもより高速でスケーラブルであると考えられます。
  • 信頼性の向上。並行処理クラスの開発は困難です。Java言語により提供される低レベルの並行処理プリミティブ(synchronizedvolatilewait()notify()およびnotifyAll())は適切に使用することが困難で、これらの機能を使用した場合に発生するエラーの検出およびデバッグは困難です。標準化され、幅広くテストされた並行構築ブロックを使用することにより、デッドロック、欠乏状態、競合状態、または過度のコンテキスト・スイッチングなどのスレッド化の危険を引き起こす潜在的な原因が取り除かれます。並行処理ユーティリティはデッドロック、欠乏状態、競合状態を注意深く検査しました。
  • 保守容易性の向上。標準ライブラリ・クラスを使用するプログラムは、複雑で独自のクラスに依存するプログラムよりも理解や管理が簡単です。
  • 生産性の向上。開発者はすでに標準ライブラリ・クラスを理解していると考えられるので、APIおよびアドホックな並行処理コンポーネントの動作を学ぶ必要はありません。さらに、並行処理アプリケーションは信頼性が高く、十分テストされたコンポーネントの上で構築された場合はデバッグが簡単になります。

つまり、並行アプリケーションの実装に並行処理APIを使用すると、自分のプログラムをより明確に、より短く、より高速にできます。信頼性も高く、スケーラブルで、書くことも読むことも、また管理も簡単です。

並行処理APIには次のものが含まれます。

表14-1 並行処理API

API 説明
仮想スレッド 仮想スレッドは、高スループットの同時アプリケーションの書込み、メンテナンスおよびデバッグの労力を減らす軽量スレッドです。
構造化並行性 構造化並行性は、異なるスレッドで実行されている関連タスクのグループを1つの作業単位として処理することにより、エラーの処理と取消しを合理化し、信頼性と可観測性を向上させます。
タスク・スケジューリング・フレームワーク Executorインタフェースは、一連の実行ポリシーに基づいて非同期なタスクの呼出し、スケジューリング、実行、管理を標準化します。送信スレッド内、Swingのイベントのような単一バックグラウンドのスレッド(「Executors::newSingleThreadExecutor」を参照)、新しく作成されたスレッド、またはスレッド・プール(「Executors::newFixedThreadPool」を参照)で実行されるタスクを許可する実装が提供され、開発者は任意の実行ポリシーをサポートするExecutorのカスタマイズした実装を作成できます。組込みの実装は、キューの長さ制限や飽和ポリシー(「RejectedExecutionHandler」を参照)などの構成可能なポリシーを提供し、リソースの無駄な使用を防ぐことによってアプリケーションの安定性を向上させます。
分岐/結合フレームワーク このフレームワークは、ForkJoinPoolクラスに基づくExecutorの実装です。ワーカー・スレッドのプールを使用して、多数のタスクを効率的に実行するように設計されています。work-stealing技術を使用して、すべてのワーカー・スレッドをビジーに維持し、複数のプロセッサを最大限に活用します。
並行処理コレクション 並行処理コレクションには、QueueBlockingQueue、およびBlockingDequeが含まれます。
原子変数 単一変数(プリミティブ型または参照)を原子的に操作して、高性能の基本算術演算と比較設定メソッドを提供するためのユーティリティ・クラスが用意されています。java.util.concurrent.atomicパッケージの原子変数の実装は、ほとんどのプラットフォームにおいて、同期の使用により可能になるパフォーマンスより高いパフォーマンスを提供し、高性能の並行処理アルゴリズムの実装と同時に、カウンタとシーケンス番号のジェネレータを簡便に実装します。
シンクロナイザ シンクロナイザは、スレッド間の調整を容易にする汎用同期クラスです。SemaphoreCyclicBarrierCountdownLatchPhaser、およびExchangerが含まれます。
ロック ロッキングはsynchronizedキーワードを介してJava言語に組み込まれますが、組込みモニター・ロックに対する多くの制限があります。java.util.concurrent.locksパッケージは同期と同様のメモリー・セマンティクスを使用する高性能のロック実装を提供します。さらに、ロック、ロックごとの複数の条件変数の獲得、複数のロックの入れ子を使用しない(hand-over-hand)保持を試みるときのタイムアウトの指定もサポートし、ロックを獲得するために待機している割込みスレッドをサポートします
ナノ秒単位のタイミング System.nanoTimeメソッドは、相対的な時間を測定するためにナノ秒の単位でタイム・ソースへのアクセスを受け入れます。タイムアウトを受け入れるメソッド(たとえば、BlockingQueue.offerBlockingQueue.pollLock.tryLockCondition.awaitおよびThread.sleep)はタイムアウト値をナノ秒単位で設定できます。System.nanoTimeメソッドの実際の精度はプラットフォームにより異なります。
スレッド・ローカル変数 スレッド・ローカル変数ThreadLocal型の変数です。「標準」変数とは異なり、スレッド・ローカル変数にアクセスする各スレッドには、独立して初期化される変数の独自のコピーがあります。