同時プログラミングのタスク&スレッドのキャンセルとオフ
4736 ワード
タスクまたはスレッドのキャンセルとクローズ
スレッドにタスクをコミットし、タスクの実行を開始すると、通常、タスクの実行が完了すると自動的に停止および終了しますが、ユーザーがキャンセル操作を行ったなど、タスクが自動的に終了する前に早期に終了したい場合が多いです.具体的には、キャンセル操作は以下のように分けられます.
ユーザー要求のキャンセル操作:例えばユーザーがキャンセルボタンを押して、管理インタフェースを通じてキャンセルを要求するなど;
タイムアウト停止:httpリクエストを要求したり、指定した時間に結果が返されないときにタスクをキャンセルしたりします.
アプリケーション・イベント:迷路の出口を探すなど、スレッドが出口を見つけた場合、出口を探すすべてのタスクを終了します.
実行時エラー:タスクが実行中にエラーが発生した場合など、タスクスレッドのキャンセルと停止のポリシーが必要です.
アプリケーションのシャットダウン:例えば、Webサービスを提供するクラスタでは、あるマシンのパフォーマンスが非常に悪く、そのマシンをシャットダウンしてチェックする必要があります.
スレッドとタスクを起動するのは簡単ですが、javaはスレッドやタスクを停止するための安全で、迅速で、信頼できるメカニズムを提供していません.次のいくつかの典型的なタスクとスレッドを停止する方法を示します.
スレッドをキャンセルまたは閉じる方法またはスレッドの方法
フラグビットがキャンセルされました.つまり、Booleanタイプの変数を指定し、その変数を設定することでタスクを停止します.
ここでの割り込みフラグビットはvolatile修飾を用いた.ロックを使わない目的は性能のためであり、volatileを加える目的は可視性を保証することであり、レジスタなどにキャッシュされた値ではなく、読むたびに最近更新された値を常に読むことができる.
わりこみ
メソッド1ではdoSomething()メソッドを実行した後にwhileにループしてcanceledの値を判断する.この方法では、ほとんどの問題を解決することができますが、doSomething()がブロックされている場合、次のループを実行することはできません.もちろん、canceledを判断してタスクをキャンセルすることはできません.たとえばdoSomething()メソッドでBlockingQueueのputメソッドまたはtakeメソッドが呼び出されます.BlockingQueueのtakeは、BlockingQueueが空の場合、queueにデータがあるまでブロックされます.従って、この場合、フラグビットによる終了ポリシーは失効し、スレッドのアクティブ性の問題も生じる.
この問題を解決するためにjavaは、タスクまたはスレッドをキャンセルするためのポリシーを中断するコラボレーションメカニズムを提供します.各スレッドには、割り込み時にtrueとなる割り込み状態(interrupted status)があります.JAvaスレッドはいくつかの割り込み方法で割り込みを処理し、interrupt()方法でスレッドを割り込み、この方法を呼び出すことは、ターゲットスレッドが必ず作業を停止することを意味するものではなく、ターゲットスレッドに割り込みメッセージを伝えただけで、作業を停止するかどうかは、ターゲットスレッドが割り込みメッセージを取得した後の処理ポリシーにも依存する.isinterrupted()は、スレッドの割り込み状態を返します.interrupted()メソッドは、スレッド割り込み状態を明確にし、前の割り込み状態を返します.このメソッドは、割り込み状態を消去する唯一の方法でもあります.
割り込み要求は、ターゲットスレッドにメッセージが発生しただけであり、ターゲットスレッドが割り込み信号を取得した後にどのようにするかは、割り込みポリシーに依存するため、カスタムスレッドの割り込みポリシーが必要である.できるだけ迅速に終了し、必要に応じてクリーンアップし、可能な場合はエンティティスレッドが終了したことを通知します.コードがスレッドの所有者でない場合(スレッドプールのスレッド所有者はスレッドプール)は、割り込み状態を慎重に保存する必要があります.
Futureでキャンセル
割り込み不可ブロックの処理
多くのブロックされたライブラリは、interruptExceptin例外を早期に返して放出することによって、割り込みに対する応答を実現しますが、すべてのブロック方法とブロックメカニズムが割り込みに応答するわけではありません.1つのスレッドがsocket I/Oを同期したり、内部ロックを待ったりしてブロックされている場合、スレッドは割り込み状態を除去できる以外に何もできません.中断不可能なアクティビティによってブロックされるスレッドについては、タイプの中断応答の方法を提供して処理することができるが、スレッドがどのような理由でブロックされているのかを理解してから、病状に応じて薬を処方しなければならない.次は、中断不可能なブロックを処理するいくつかの方法です.
1.java ioの同期io、最も一般的なブロックIOはsocketの読み取りと書き込みであるが、inputstreamとoutputstreamのread、writeメソッドはいずれも応答中断しないが、下位層のsocketを閉じることでread、writeにsocketExceptionを投げ出すことができる.
2.java nioの同期ioで、iterruptibleChannelを待つスレッドを中断すると、ClosedByInterruptExceptionが放出され、リンクが閉じます(このリンクで他のスレッドがブロックされ、ClosedByInterruptExceptionが放出されます).1つの待機iterruptibleChannelを閉じると、リンク操作にブロックされた複数のスレッドがAsynchronousCloseExceptionから放出されます.ほとんどの標準的なchannelはiterruptibleChannelを実現しています.
3 Selectorの非同期I/OスレッドがSelector.selectメソッドにブロックされている場合、closeメソッドはClosedSelectorExceptionを放出することによって早期に戻ります.
4.ロックを取得します.内部ロックを使用している場合は、最終的にロックが得られることを保証できない場合は、それを終了することはできません.しかし、明示的なLockクラスはLockInterruptibleメソッドを提供し、ロックを待つと同時に割り込みに応答することができる.
ExecutorServiceクローズ
ExectuorServiceでは、shutdownとshutdownNowの2つの閉じる方法が用意されています.shutdownはキュー内のタスクをすべて処理した後に優雅に閉じることができるので、セキュリティが高く、応答が遅いのが欠点です.shutdownNowはExectuorServiceを強制的に閉じるため、応答速度が速く、安全ではなく、データ構造を破壊する可能性があるという欠点があります.
致命毒丸
致命的な毒丸は生産者の消費者モデルを閉鎖する方法である.生産者キューに特殊なオブジェクトを入れ、消費者が特殊なオブジェクトを取得した後、スレッドを閉じ始めます.この方式は消費者に致命的な毒丸を入れた後、新しい任務を入れるべきではないことを要求している.致命毒丸は、生産者の消費者スレッドデータが既知である場合に使用しなければならない.多生産者モードでは、各生産者は致命毒丸を入れ、消費者がN番目(生産者の数)の毒丸を受け入れた後、閉鎖操作を開始する.マルチ消費者モードでは、生産者にN(消費者の数)毒丸を入れさせるが、このような状況では消費者が処理任務をポーリングしなければならない.そうしないと、消費者一人一人が毒丸を得ることを保証できない.致命毒丸の欠点の一つは、多消費者多生産者モデルの下ではあまり応用されず、一定の限界があることだ.
スレッドにタスクをコミットし、タスクの実行を開始すると、通常、タスクの実行が完了すると自動的に停止および終了しますが、ユーザーがキャンセル操作を行ったなど、タスクが自動的に終了する前に早期に終了したい場合が多いです.具体的には、キャンセル操作は以下のように分けられます.
ユーザー要求のキャンセル操作:例えばユーザーがキャンセルボタンを押して、管理インタフェースを通じてキャンセルを要求するなど;
タイムアウト停止:httpリクエストを要求したり、指定した時間に結果が返されないときにタスクをキャンセルしたりします.
アプリケーション・イベント:迷路の出口を探すなど、スレッドが出口を見つけた場合、出口を探すすべてのタスクを終了します.
実行時エラー:タスクが実行中にエラーが発生した場合など、タスクスレッドのキャンセルと停止のポリシーが必要です.
アプリケーションのシャットダウン:例えば、Webサービスを提供するクラスタでは、あるマシンのパフォーマンスが非常に悪く、そのマシンをシャットダウンしてチェックする必要があります.
スレッドとタスクを起動するのは簡単ですが、javaはスレッドやタスクを停止するための安全で、迅速で、信頼できるメカニズムを提供していません.次のいくつかの典型的なタスクとスレッドを停止する方法を示します.
スレッドをキャンセルまたは閉じる方法またはスレッドの方法
フラグビットがキャンセルされました.つまり、Booleanタイプの変数を指定し、その変数を設定することでタスクを停止します.
class CancelableTask extends Thread {
praivate volatile boolean canceled =false;
public void run() {
while(!canceled ) {
doSomething();
}
}
public void cancel(){canceled = true;}
}
ここでの割り込みフラグビットはvolatile修飾を用いた.ロックを使わない目的は性能のためであり、volatileを加える目的は可視性を保証することであり、レジスタなどにキャッシュされた値ではなく、読むたびに最近更新された値を常に読むことができる.
わりこみ
メソッド1ではdoSomething()メソッドを実行した後にwhileにループしてcanceledの値を判断する.この方法では、ほとんどの問題を解決することができますが、doSomething()がブロックされている場合、次のループを実行することはできません.もちろん、canceledを判断してタスクをキャンセルすることはできません.たとえばdoSomething()メソッドでBlockingQueueのputメソッドまたはtakeメソッドが呼び出されます.BlockingQueueのtakeは、BlockingQueueが空の場合、queueにデータがあるまでブロックされます.従って、この場合、フラグビットによる終了ポリシーは失効し、スレッドのアクティブ性の問題も生じる.
この問題を解決するためにjavaは、タスクまたはスレッドをキャンセルするためのポリシーを中断するコラボレーションメカニズムを提供します.各スレッドには、割り込み時にtrueとなる割り込み状態(interrupted status)があります.JAvaスレッドはいくつかの割り込み方法で割り込みを処理し、interrupt()方法でスレッドを割り込み、この方法を呼び出すことは、ターゲットスレッドが必ず作業を停止することを意味するものではなく、ターゲットスレッドに割り込みメッセージを伝えただけで、作業を停止するかどうかは、ターゲットスレッドが割り込みメッセージを取得した後の処理ポリシーにも依存する.isinterrupted()は、スレッドの割り込み状態を返します.interrupted()メソッドは、スレッド割り込み状態を明確にし、前の割り込み状態を返します.このメソッドは、割り込み状態を消去する唯一の方法でもあります.
class InterruptableTask extends Thread {
public void run() {
try {
while(!thread.currentThread.isInterruped())
doSomeThing();
} catch(InterruptException e) {
exitThread();
}
}
public void cancel() {intertupt();}
}
割り込み要求は、ターゲットスレッドにメッセージが発生しただけであり、ターゲットスレッドが割り込み信号を取得した後にどのようにするかは、割り込みポリシーに依存するため、カスタムスレッドの割り込みポリシーが必要である.できるだけ迅速に終了し、必要に応じてクリーンアップし、可能な場合はエンティティスレッドが終了したことを通知します.コードがスレッドの所有者でない場合(スレッドプールのスレッド所有者はスレッドプール)は、割り込み状態を慎重に保存する必要があります.
Futureでキャンセル
public void futureTask() throws ExecutionException {
Future<?> task = taskExec.submit();
try{
task.get(timeout,unit);
} catch(TimeoutExecption e){
doSomeClear();
}catch(ExecutionException e){
throw ExecutionException ;
} finally {
task.cancel(true);
}
}
割り込み不可ブロックの処理
多くのブロックされたライブラリは、interruptExceptin例外を早期に返して放出することによって、割り込みに対する応答を実現しますが、すべてのブロック方法とブロックメカニズムが割り込みに応答するわけではありません.1つのスレッドがsocket I/Oを同期したり、内部ロックを待ったりしてブロックされている場合、スレッドは割り込み状態を除去できる以外に何もできません.中断不可能なアクティビティによってブロックされるスレッドについては、タイプの中断応答の方法を提供して処理することができるが、スレッドがどのような理由でブロックされているのかを理解してから、病状に応じて薬を処方しなければならない.次は、中断不可能なブロックを処理するいくつかの方法です.
1.java ioの同期io、最も一般的なブロックIOはsocketの読み取りと書き込みであるが、inputstreamとoutputstreamのread、writeメソッドはいずれも応答中断しないが、下位層のsocketを閉じることでread、writeにsocketExceptionを投げ出すことができる.
2.java nioの同期ioで、iterruptibleChannelを待つスレッドを中断すると、ClosedByInterruptExceptionが放出され、リンクが閉じます(このリンクで他のスレッドがブロックされ、ClosedByInterruptExceptionが放出されます).1つの待機iterruptibleChannelを閉じると、リンク操作にブロックされた複数のスレッドがAsynchronousCloseExceptionから放出されます.ほとんどの標準的なchannelはiterruptibleChannelを実現しています.
3 Selectorの非同期I/OスレッドがSelector.selectメソッドにブロックされている場合、closeメソッドはClosedSelectorExceptionを放出することによって早期に戻ります.
4.ロックを取得します.内部ロックを使用している場合は、最終的にロックが得られることを保証できない場合は、それを終了することはできません.しかし、明示的なLockクラスはLockInterruptibleメソッドを提供し、ロックを待つと同時に割り込みに応答することができる.
ExecutorServiceクローズ
ExectuorServiceでは、shutdownとshutdownNowの2つの閉じる方法が用意されています.shutdownはキュー内のタスクをすべて処理した後に優雅に閉じることができるので、セキュリティが高く、応答が遅いのが欠点です.shutdownNowはExectuorServiceを強制的に閉じるため、応答速度が速く、安全ではなく、データ構造を破壊する可能性があるという欠点があります.
致命毒丸
致命的な毒丸は生産者の消費者モデルを閉鎖する方法である.生産者キューに特殊なオブジェクトを入れ、消費者が特殊なオブジェクトを取得した後、スレッドを閉じ始めます.この方式は消費者に致命的な毒丸を入れた後、新しい任務を入れるべきではないことを要求している.致命毒丸は、生産者の消費者スレッドデータが既知である場合に使用しなければならない.多生産者モードでは、各生産者は致命毒丸を入れ、消費者がN番目(生産者の数)の毒丸を受け入れた後、閉鎖操作を開始する.マルチ消費者モードでは、生産者にN(消費者の数)毒丸を入れさせるが、このような状況では消費者が処理任務をポーリングしなければならない.そうしないと、消費者一人一人が毒丸を得ることを保証できない.致命毒丸の欠点の一つは、多消費者多生産者モデルの下ではあまり応用されず、一定の限界があることだ.