第五章:ビルディングブロック
プラットフォームのライブラリは、スレッドセーフなコンテナと同期ツール(の様々なとして同時ビルディング・ブロックの豊富なセットが含まれていSynchronizer
)。
Synchronizer
協調スレッド間の制御の流れを調節するため。
同期コンテナ
同期コンテナは二つの部分、1から成りVector
且つHashTable
早期JDKの一部であり、他方は、それらの同族体容器のみJDK1.2同期パッケージ(に加えているwrapper
)クラス。
これらのクラスは、Collections.synchronizedXxx
ファクトリメソッドを作成します。
その状態をカプセル化することにより、これらのクラスの状態、および1つのスレッドのみのコンテナにアクセスできるように、スレッドセーフを達成するために、すべてのパブリックメソッドを同期させます。
同期コンテナ新たな問題
同期コンテナは、スレッドセーフです。しかし、複雑な操作のために、時にはあなたは、追加のクライアントロック(クライアントサイドロック)保護を使用する必要があるかもしれません。
複合容器は、一般的に含む:例えば(PUT-IF-「すなわち添加欠く」として反復(繰り返し容器内の最後の要素まで取得した要素)、ナビゲーション(特定の順序に応じて次の要素を見つけるために)、動作状態を、存在しない)、そこ鍵K地図、そうでない場合、マッピング(K、V)を追加するかどうかをチェック。
同期コンテナでは、クライアントが保護をロックしていない場合でも、これらの複雑な操作は、技術的にはスレッドセーフではなくなったコンテナの同時変更を他のスレッドには、彼らはあなたが期待するように動作しない場合があります。
/**
* 操作Vector的复合操作可能导致混乱的结果
*/
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
/**
* 使用客户端加锁,对Vector进行复合操作
*/
public static Object getLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex) ;
}
}
public static void deleteLast(Vector list) {
synchronized (list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
コンカレント・コンテナー
同期クラスを改善するために、コンテナによるJava5.0コンテナは、複数の同時を提供します。同時マルチスレッドコンテナは、同時アクセスのために設計されています。
- Javaの5.0が追加され
ConcurrentHashMap
、同期を達成するために、ハッシュマップを交換します - 大部分の動作は、読み出し動作である場合
CopyOnWriteArrayList
代わりに対応する同期実装リストの
新しいConcurrentMap
インターフェースは、「それは(PUT-IF-不在)に追加され行方不明」として、一般的な複雑な操作のサポートを追加し 、 交換し、削除条件。
また同期コンテナコンカレント・コンテナー、このアプローチにはほとんどリスクが有意に増加スケーラビリティをもたらしています。
Javaの5.0は、2つの新しいコンテナタイプを追加されますQueue
とBlockingQueue
。
Queue
一時的に一連の要素を保持するために使用される更なる処理を待っています。基づいたQueue
具体的な実装のシリーズ、。BlockingQueue
それは、拡張Queue
閉塞が挿入され、取得操作をすることができます増加し、。キューをブロックすると、生産者と消費者の設計に非常に有用です。
ただ、ConcurrentHashMap
同時ハッシュマップの代替の同期として、Java 6の追加ConcurrentSkipListMap
とConcurrentSkipListset
同期として使用SortedMap
し、SortedSet
(のような同時代替synchronizedMap
パッケージTreeMap
またはTreeSet
)。
ConcurrentHashMapの
ConcurrentHashMap
そして、HashMap
ハッシュテーブルのように、それは完全に異なるロック戦略を使用して、私たちはより良い並行性とスケーラビリティを提供することができます。
ConcurrentHashMap
分離ロックと呼ばれる、より詳細なロック機構を、使用してください。このメカニズムは、共有アクセスのより深いレベルを可能にします。同時読み取りの任意の数のスレッドが地図にアクセスすることができ、読者と作家はまた、同時アクセスマップと書き込み同時スレッドの数が限られても、地図を修正することができます。その結果、シングルスレッドのアクセスのパフォーマンスをほとんど喪失しながら、同時アクセスのためのより高いスループットにつながります。
ブロッキングキューとプロデューサー - 消費者モデル
(キューをブロックするBlockingQueue
)ブロックを提供put
し、take
方法を、彼らはタイムアウトすることが可能offer
とpoll
等価です。
ブロッキングキュー・サポート・プロデューサー - 消費者のデザインパターン。プロデューサー - 消費者のデザインはと「認識が行われる必要があり、」単離された「実装を。」このモードでは、ジョブは後処理を準備するために、リスト(「やって」)を直ちに処理が、仕事へのタスクになります見つけることができません。
異なる速度を生成したり、データと消費者を変更するには、生産者と消費者、生産者 - 消費者モデルは、このように管理ワークロードを簡素化し、これらの活動を分離します。
両端キューと作業盗難
同じJava 6の二つの新しい容器の種類、Deque
(顕著デッキ)とBlockingDeque
これをそれぞれ展開されるQueue
とBlockingQueue
。
Deque
ある両端キュー、それぞれ、効率的に挿入し、頭と尾を除去することができます。彼らはそれを実現ArrayDeque
してLinkedBlockingDeque
。
自分自身と呼ばれるダブルエンドキューと同様に、消費者のモデル-プロデューサーに適用キューを遮断するなどの作業を盗む関連するパターン(盗む仕事を)を。
消費者のデザインプロデューサーは、すべての消費者が唯一の作業キューを共有し、デザインの仕事を盗んで、各顧客は自身の両端キューを持っています。消費者は、自分自身ダブルエンドキューすべての作業を完了するためにした場合、それはダブルエンドのタスクキューの最後に、他の消費者を盗むことができます。
ワーカースレッドではなく、共有タスクキューと競合するので、そのプロデューサーの伝統的な動作モードよりも盗もう - 消費者のデザインスケーラビリティ、時間のほとんどは、彼らが競争を減らし、その両端キューにアクセスします。
労働者が別のキューにアクセスするために持っている場合は、それによってさらに両端キューのための競争を減らすことではなく、頭からよりも、尾から取得されます。
シンクロナイザー
Synchronizer
スレッド自体の状態に応じて流量調整弁を制御するオブジェクトです。
キューをブロックすると、再生することができますSynchronizer
役割を、他の種類のSynchronizer
セマフォ(セマフォ)、ポイント(バリア)とラッチ(ラッチ)を含みます、。
プラットフォームのライブラリがいくつかに存在するSynchronizer
クラス、彼らはあなたのニーズを満たすことができないかのように、第14章で説明するように、また、あなた自身のものを作成、従うことができますSynchronizer
。
全てがSynchronizer
類似の構造特性と題されている:それらはある時点でスレッドの実行を決定する状態とこれらの状態をカプセル化通過または待つことを余儀なくされ、それらはまた状態制御方法を提供し、効率的な待ちSynchronizer
所望の状態にする方法。
ロックアウト
ラッチ(ラッチ)であるSynchronizer
スレッドが終了(終端)状態に達するまで、それはスレッドの進行を遅らせることができます。
ロックはドアのように動作します:ロックアウトが終了状態に到達するまで、ドアが閉じられた、何のスレッドが状態の終わりに、渡すことはできませんすべてのスレッドが通過できるように、ドアが開いて、来ます。
:閉鎖症のような、他の活動の完了後まで、特定の活動が行われていることを確実にするために使用することができます
- それは必要なリソースが初期化されるまで、計算が実行されていないことを確認してください。バイナリ(二状態)をラッチする、「リソースRを初期化された」Rを発現するために使用することができ、必要な活動の全ては、第一閉鎖に待たなければなりません。
- 他のサービスがそれに依存しているまで、サービスは開始されないことを確実にするために、すでに始まっています。各サービスは、関連するバイナリラッチが含まれています; Sは、最初のロックで他のサービスを待つためにサービス開始を開きます依存するすべてのサービスには、プロセスを開始することができますので、S、開始後、ロックがS解放され、Sを依存しています。
- 活動のすべての部分まで待っては完全にすべての準備ができているかどうか、このようなマルチプレイヤーゲーム内のすべての選手として、処理を継続するために用意されています。すべてのプレイヤーが準備ができている時にもこのようなロックは、終了状態に達します。
CountDownlatch
可撓ロックはこれらの場合のそれぞれについて、達成され、スレッドが待機以上発生するイベントの集合を可能にします。
ブロッキング状態は、イベントの数を待つ必要性を示すために使用正の数に初期化カウンタが含まれます。
カウントダウンイベントを示すためにカウンタをデクリメントの操作の方法が発生し、カウンタが到達するためのawait方法待機ゼロ、すべてのイベントを待つために行われた時点れます。カウンタの値が非ゼロのエントリである場合は、カウンタがゼロである、または待機スレッドが中断され、タイムアウトになるまで、のawaitメソッドはブロックします。
FutureTask
FutureTask
また、ブロックとして使用することができます。(FutureTask
抽象の実装は、結果を計算するポータブルを説明します)。
FutureTask
それは、によって計算されるCallable
ポータブルの結果に相当し、実装、Runnable
および三つの状態があります:待ち時間は、動作を終了します。
完了し、正常、異常及びキャンセルの端部を含む任意の方法ですべての計算に端を含みます。いったんFutureTask
完了状態に入る、それがこの状態で停止することはありません。
Future.get
動作はタスクの状態に依存します。それが完了している場合、get
あなたはすぐにその結果を返し、それ以外の場合は、タスクが完了状態に転送されるまでブロックされます、結果を取り戻すか、例外をスローすることができます。
FutureTask
:計算結果は、計算結果がこのスレッドを必要とする実行するスレッドから転送されたFutureTask
規程は、セキュリティが上記の結果に基づいて構築されたこの配信を解放することを確認します。
/**
* 使用FutureTask预载稍后需要的数据
*/
public class Preloader {
private final FutureTask<ProductInfo> future = new FutureTask<ProductInfo>(new Callable<ProductInfo>() {
public ProductInfo call() throws DataLoadException {
return loadProductInfo();
}
});
private final Thread thread = new Thread(future);
public void start() {
thread.start();
}
public ProductInfo get() throws DataLoadException, InterruptedException {
try {
return future.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof DataLoadException)
throw (DataLoadException) cause;
else
throw launderThrowable(cause);
}
}
}
点数
レベル(barrier
ロックアウトと同様に)、彼らは、特定のイベントが発生するまで、スレッドのグループをブロックすることができました。
主な違いは、ロックポイントは、すべてのスレッドが処理を継続するために、同一のチェックポイントに到達しなければならないことです。
閉鎖症は、イベントを待っている、チェックポイントは、他のスレッドを待っています。
:.モール内の指定されたコレクションの場所のいくつかの家族として、達成するためのプロトコルレベル「私たちはそれぞれが未来へのないままにする、マクドナルド6:00で確認した後、私たちは次に何をすべきかを決定します。」
CyclicBarrier
並列反復アルゴリズムのために非常に有用である1つのチェックポイント、中に所定回数のメンバーを許可する、アルゴリズムは、独立したサブ一連の問題に問題を分割します。
スレッドがチェックポイント、コールのawaitに到達すると、すべてのスレッドがチェックポイントに到達するまで、のawaitがブロックされます。すべてのスレッドがチェックポイントに達している場合は、チェックポイントが正常に突破されたので、すべてのスレッドがリリースされていることを、レベルは次の使用のための準備にリセットされます。
あなたが待機するタイムアウトを呼び出すか、スレッドをブロックすると、中断された場合、ポイントは失敗ではなく、経由のawait完了までのすべてのコールとみなされますBrokenBarrierException
終了。
あなたが成功したレベルを渡す場合は、await
スレッドごとに戻りますが、一意のインデックス番号に達し、あなたがリーダーを生成するために、「選挙」にそれを使用することができ、次の反復は、いくつかの特別な仕事を引き受けます。
/**
* 在一个细胞的 自动系统中用CyclicBarrier协调计箅
*/
public class CellularAutomata {
private final Board mainBoard;
private final CyclicBarrier barrier;
private final Worker[] workers;
public CellularAutomata(Board board) {
this.mainBoard = board;
int count = Runtime.getRuntime().availableProcessors();
this.barrier = new CyclicBarrier(count, () -> mainBoard.commitNewValues());
this.workers = new Worker[count];
for (int i = 0; i < count; i++) {
workers[i] = new Worker(mainBoard.getSubBoard(count, i));
}
}
private class Worker implements Runnable {
private final Board board;
public Worker(Board board) {
this.board = board;
}
@Override
public void run() {
while (!board.hasConverged()) {
for (int x = 0; x < board.getMaxX(); x++) {
for (int y = 0; y < board.getMaxY(); y++) {
board.setNewValue(x, y, computeValue(x, y));
}
}
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
public void start() {
for (int i = 0; i < workers.length; i++) {
new Thread(workers[i]).start();
}
mainBoard.waitForConvergence();
}
}
}