タスクをキャンセルできるかどうかは、実際にはその実装に依存します。通常、フラグを継続するかどうかを断続的にチェックします。
このようなフラグを自分で実装し、それを設定するメソッドを実装できます:
private volatile boolean shouldStop;
public void cancel() {
shouldStop = true;
}
@Override
public void run() {
while (!shouldStop) {
// do work
}
}
しかし、スレッドにはすでにフラグが付いています: 割り込みフラグです。また、必ずしもスレッドをキャンセルするために使用されるわけではありませんが、まさにその目的で使用されるのが一般的です。実際、標準のExecutorService
実装では、スレッドを中断してスレッドをキャンセルしようとします。
それとは別に、いくつかのブロッキング メソッド (スレッドを状態BLOCKED
またはWAITING
状態にするメソッド) は、スレッドが中断されたときに をスローし、InterruptedException
その時点でRUNNABLE
再び になります。これは、ブール値フラグを使用した以前のアプローチでは達成できないことです。
したがって、割り込みを使用してタスクをキャンセルできるようにする方が良い方法です。また、その cancel() メソッドももう必要ありません。
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// do work
}
}
おまけとして、スレッドを知っているコードは、スレッドをキャンセルする方法を知っています。ExecutorService
標準実装を含みます。
InterruptedException
をキャッチすると中断フラグがクリアされるため、 をキャッチするときは注意が必要です。例外をキャッチするときは常に中断フラグを復元することをお勧めします。これにより、クライアントは自分が行っていることをやめる時が来たこともわかります。
private BlockingQueue<Integer> queue;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Integer id = queue.take(); // blocking method
// do work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
スレッドをキャンセルするには、Thread
オブジェクトへの参照を保持して呼び出しますinterrupt()
。
Thread thread = new Thread(new InterruptibleTask());
thread.start();
// some time after :
thread.interrupt();
しかし、より洗練されたアプローチは、オブジェクトを介してタスク (タスクが実行されている特定のスレッドではなく) を監視するFuture
ことです。Runnable
これを行うには、またはCallable
を でラップしますFutureTask
。
RunnableFuture<Void> task = new FutureTask<>(new InterruptibleTask(), null);
new Thread(task).start();
// some time after :
task.cancel(true); // true indicating interruption may be used to cancel.
AFuture
は、タスクを制御する上で重要です。完了するまで待機し、オプションでタスクが計算した値を受け取ることができます。
try {
String value = future.get(); // return value is generically typed String is just as example.
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // since future.get() blocks
} catch (ExecutionException e) {
logger.log(Level.SEVERE, "Exception on worker thread", e.getCause()); // the ExecutionException's cause is the Exception that occurred in the Task
}
複数のタスク (または 1 つだけでも) がある場合は、 ExecutorService を使用する価値があります。
ExecutorService pool = Executors.newCachedThreadPool();
Future<?> submit = pool.submit(new InterruptibleTask());
pool.shutdownNow(); // depending on ExecutorService implementation this will cancel all tasks for you, the ones Executors returns do.