mizdra's blog

ぽよぐらみんぐ

【Java】 Future.cancel() は起動中のタスクを強制終了せずに true を返す

どうも、mizdraです。
前回に引き続きJavaの記事です。
今回はスレッドプールと併用されるFutureクラスの cancel() について書いていきます。


まず初めに、Java APIドキュメントで Future.cancel() の説明を見てみましょう。

このタスクの実行の取消しを試みます。タスクがすでに完了していた場合、すでに取り消されていた場合、またはその他の理由で取消しできなかった場合、この試みは失敗します。その試みが成功し、cancelの呼出し時にこのタスクが起動しなかった場合、このタスクが実行されることはありません。タスクが起動済みの場合は、このタスクの停止を試みる際、このタスクを実行しているスレッドに割り込む必要があるかどうかは、mayInterruptIfRunningパラメータで判断します。
このメソッドが復帰すると、その後のisDone()の呼出しは常にtrueを返します。このメソッドがtrueを返した場合、後続のisCancelled()の呼出しは常にtrueを返します。


パラメータ:
mayInterruptIfRunning - このタスクを実行しているスレッドに割り込む必要がある場合はtrue、そうでない場合は、実行中のタスクを完了できる


戻り値:
タスクを取り消せなかった場合はfalse (通常はタスクがすでに正常に完了していたため)、そうでない場合はtrue


Future (Java Platform SE 8)


気になることが一点あります。
それは、「タスクが起動済みの時にこのメソッドを実行したら戻り値はどうなるのか。」ということです。
この説明を読む限り、 Future.cancel() をしても起動中のタスクは強制終了することなく、最後まで処理を実行することになります。
タスクが起動中にも関わらず、 Future.cancel() は停止が成功したことを示す true を返すのか、それもと false を返すのか
それでは早速サンプルコードを書いて試してみましょう。

Future.cancel() は起動中のタスクを強制終了せずに true を返す


実行結果

index: 0, message: start
index: 2, message: start
index: 1, message: start
index: 0, isCancelled: false, isDone: false
index: 1, isCancelled: false, isDone: false
index: 2, isCancelled: false, isDone: false
index: 0, cancel: true, isCancelled: true, isDone: true
index: 1, cancel: true, isCancelled: true, isDone: true
index: 2, cancel: true, isCancelled: true, isDone: true
index: 2, message: end
index: 0, message: end
index: 1, message: end


普通に Future.cancel() は true を返してますね。
念のため、 Future.cancel(true) でもやってみます。

実行結果

index: 1, message: start
index: 2, message: start
index: 0, message: start
index: 0, isCancelled: false, isDone: false
index: 1, isCancelled: false, isDone: false
index: 2, isCancelled: false, isDone: false
index: 0, cancel: true, isCancelled: true, isDone: true
index: 0, message: InterruptedException
index: 1, message: InterruptedException
index: 1, cancel: true, isCancelled: true, isDone: true
index: 2, cancel: true, isCancelled: true, isDone: true
index: 2, message: InterruptedException

こちらも true を返してます。
このサンプルとドキュメントから判明したことをまとめてみると以下のようになります。

  • タスクがすでに完了していた、すでに取り消されていたなどの場合は Future.cancel() はfalse を返す。
  • "タスクを取り消す"というのは起動中のタスクを強制停止するわけではなく、そのタスクは run() が呼び出し終わるまで起動し続ける。
  • 取り消したタスクが起動中であっても、 Future.cancel() は true を返す。

タスクの完了まで待機する

タスクが完了してから特定の処理をしたい場合、 起動中のタスクを強制終了出来ない Future.cancel() とは他の方法で実現する必要があります。
Thread.join() のようなメソッドはFutureクラスにはないので、Future.get() を使って実装します。

必要に応じて計算が完了するまで待機し、その後、計算結果を取得します。


戻り値:
計算結果


例外:
CancellationException - 計算が取り消された場合
ExecutionException - 計算で例外がスローされた場合
InterruptedException - 待機中に現在のスレッドで割込みが発生した場合


Future (Java Platform SE 8)

本来ならCallableインターフェイスを実装したクラスのためのメソッドですが、計算が完了するまで待機する目的ならRunnableインターフェイスにも利用できます。


実行結果

index: 0, message: start
index: 2, message: start
index: 1, message: start
index: 0, isCancelled: false, isDone: false
index: 1, isCancelled: false, isDone: false
index: 2, isCancelled: false, isDone: false
index: 2, message: end
index: 0, message: end
index: 1, message: end
index: 0, cancel: false, isCancelled: false, isDone: true
index: 1, cancel: false, isCancelled: false, isDone: true
index: 2, cancel: false, isCancelled: false, isDone: true