Future とエラー処理
タイムアウト付きで Future.get を使い、ExecutionException を処理し、実行中のタスクをキャンセルします。
「Future とエラー処理」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
Futureとは
Future<V>は、非同期計算の完了待ちの結果を表します。完了状態の確認、結果の待機、エラー処理のためのメソッドを提供します。
import java.util.concurrent.*;
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
Thread.sleep(1000);
return 42;
});
System.out.println("Is done? " + future.isDone()); // false
Integer result = future.get(); // blocks
System.out.println("Result: " + result); // 42
pool.shutdown();タイムアウト付きfuture.get()
無期限に停止することを防ぐため、必ずタイムアウトを指定する形式を使用してください。
try {
Integer result = future.get(5, TimeUnit.SECONDS);
System.out.println(result);
} catch (TimeoutException e) {
System.out.println("Timed out");
future.cancel(true); // interrupt the task
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
System.out.println("Task threw: " + e.getCause());
}ExecutionException
タスクが例外をスローした場合、future.get()はその例外をExecutionExceptionでラップします。getCause()で元の例外を取り出します。
Future<String> failing = pool.submit(() -> {
throw new IllegalArgumentException("bad input");
});
try {
failing.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
System.out.println(cause.getClass().getSimpleName()); // IllegalArgumentException
System.out.println(cause.getMessage()); // bad input
}Futureのキャンセル
cancel(mayInterruptIfRunning)はタスクのキャンセルを試みます。成功するとtrueを返します。mayInterruptIfRunningがtrueの場合、実行中のスレッドに割り込みが送信されます。
Future<String> f = pool.submit(() -> {
try {
Thread.sleep(10_000); // long task
return "done";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "interrupted";
}
});
boolean cancelled = f.cancel(true); // send interrupt
System.out.println("Cancelled: " + cancelled); // true
System.out.println("Is cancelled: " + f.isCancelled()); // trueisCancelledとisDoneの違い
isDone()は、タスクが正常に完了した場合、例外をスローした場合、またはキャンセルされた場合にtrueを返します。isCancelled()は、キャンセルされた場合にのみtrueを返します。
// After cancel:
System.out.println(f.isDone()); // true
System.out.println(f.isCancelled()); // true
// After normal completion:
Future<Integer> done = pool.submit(() -> 5);
done.get(); // wait
System.out.println(done.isDone()); // true
System.out.println(done.isCancelled()); // false利用可能になった結果を取得するCompletionService
ExecutorCompletionServiceはプールをラップし、次に完了したFutureを取得するtake()メソッドを提供します。取得順序は送信順序ではなく、完了順序です。
ExecutorCompletionService<Integer> cs =
new ExecutorCompletionService<>(pool);
for (int i = 0; i < 5; i++) {
final int delay = 5 - i; // submit fastest last
cs.submit(() -> { Thread.sleep(delay * 100); return delay; });
}
for (int i = 0; i < 5; i++) {
Future<Integer> f = cs.take(); // next completed
System.out.println(f.get());
}複数のFutureを安全に処理する
多数のFutureから結果を収集する場合は、各Futureを個別に反復処理し、例外も個別に処理します。
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final int id = i;
futures.add(pool.submit(() ->
id % 2 == 0 ? "ok_" + id : (() -> { throw new RuntimeException("fail_"+id); }).get()
));
}
for (Future<String> f : futures) {
try {
System.out.println(f.get());
} catch (ExecutionException e) {
System.out.println("Error: " + e.getCause().getMessage());
}
}プールのシャットダウン後のFuture.get()
shutdown()の後にタスクを送信すると、RejectedExecutionExceptionがスローされます。ただし、シャットダウン前に送信したFutureは、シャットダウン後も取得できます。
ExecutorService pool2 = Executors.newFixedThreadPool(2);
Future<Integer> f = pool2.submit(() -> 99);
pool2.shutdown(); // no new tasks
// Still valid — task was submitted before shutdown:
System.out.println(f.get()); // 99
try {
pool2.submit(() -> 0); // throws!
} catch (RejectedExecutionException e) {
System.out.println("Pool shut down");
}FutureTask:手動Future
FutureTask<V>はRunnableとFuture<V>の両方を実装します。ExecutorServiceを使わずにFutureが必要な場合に便利です。
FutureTask<String> task = new FutureTask<>(() -> "computed");
new Thread(task).start(); // run in any thread
System.out.println(task.get()); // computedFutureによるリトライパターン
future.get()の周囲に簡単なリトライ処理を実装します。
Future<String> future = pool.submit(() -> {
if (Math.random() < 0.7) throw new RuntimeException("transient error");
return "success";
});
for (int attempt = 0; attempt < 3; attempt++) {
try {
System.out.println(future.get());
break;
} catch (ExecutionException e) {
System.out.println("Attempt " + (attempt+1) + " failed");
if (attempt == 2) throw e;
future = pool.submit(() -> "retry"); // resubmit
}
}Futureの制限
java.util.concurrent.Futureには次の制限があります。
- コールバックがなく、get()でブロックする必要があります
- 変換処理をチェーンできません
- 複数のFutureを組み合わせられません
リアクティブでノンブロッキングな合成には、CompletableFuture(次のコース)を使用します。
確認問題
タスクが例外をスローしました。future.get()を呼び出すと、どの例外がスローされますか?
復習:Futureとエラー処理
重要なポイント:
- future.get()はブロックするため、必ずタイムアウトを指定する形式を使用します
- ExecutionExceptionはタスクの例外をラップします。getCause()で元の例外を取り出します
- cancel(true)は割り込みを送信します。isDoneとisCancelledで状態を確認します
- ExecutorCompletionService.take()は完了順にFutureを取得します
- FutureTaskはRunnableとFutureの両方を実装します
よくある質問
「Future とエラー処理」レッスンは無料ですか?
はい。「Future とエラー処理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「Future とエラー処理」で何を学びますか?
タイムアウト付きで Future.get を使い、ExecutionException を処理し、実行中のタスクをキャンセルします。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「Future とエラー処理」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ExecutorService とスレッドプールの種類
- タスクの送信:Runnable と Callable
- Future とエラー処理
- 定期タスクのための ScheduledExecutorService