0Pricing
Java Academy · レッスン

エラー処理とキャンセル

失敗を適切に伝播させます。

「エラー処理とキャンセル」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

タスクツリーで発生する失敗

サブタスクが並行して実行されると、どの分岐でもエラーが発生する可能性があります。構造化された並行性を使うと、そうした失敗を親へ伝播させるための規律ある方法が得られます。

このレッスンでは、例外、キャンセル、割り込み、タイムアウトについて扱います。

例外が現れる場所

例外をスローしたサブタスクによって、プログラムが直ちにクラッシュするわけではありません。例外はそのSubtaskハンドルに捕捉されます。例外が現れる場所は、ポリシーによって異なります。

  • ShutdownOnFailure:throwIfFailed()で現れます
  • ShutdownOnSuccess:成功したサブタスクがない場合にresult()で現れます

throwIfFailedで伝播させる

ここでは、サブタスクが失敗します。throwIfFailed()は、例外をExecutionExceptionでラップして再スローし、それをmainで捕捉します。

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            scope.fork(() -> "ok");
            scope.fork(() -> { throw new RuntimeException("boom"); });
            scope.join();
            scope.throwIfFailed();
            System.out.println("never reached");
        } catch (ExecutionException e) {
            System.out.println("Caught cause: " + e.getCause().getMessage());
        }
    }
}

独自の例外に変換する

throwIfFailedには関数を渡せます。これにより、原因を独自の例外型でラップでき、APIをすっきり保てます。

import java.util.concurrent.StructuredTaskScope;

class DataAccessException extends Exception {
    DataAccessException(Throwable cause) { super(cause); }
}

public class Main {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            scope.fork(() -> { throw new RuntimeException("db down"); });
            scope.join();
            scope.throwIfFailed(DataAccessException::new);
        } catch (DataAccessException e) {
            System.out.println("Wrapped: " + e.getCause().getMessage());
        }
    }
}

自動キャンセル

ShutdownOnFailureのもとでいずれかのサブタスクが失敗すると、スコープはスレッドに割り込みを送って兄弟サブタスクをキャンセルします。キャンセルのコードを自分で書く必要はありません。

これにより、無駄な処理や残ったままのタスクを防げます。

割り込みが仕組みになる

キャンセルは、サブタスクの仮想スレッドに割り込みを送ることで機能します。協調的に動作するコードでは、次のようにします。

  • ブロッキング呼び出しからInterruptedExceptionをスローさせます
  • 長いループでは、定期的にThread.currentThread().isInterrupted()を確認します

割り込みに応答する

このタスクはループ内で割り込みフラグを確認するため、スコープがシャットダウンしたときに早期停止できます。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().start(() -> {
            long count = 0;
            while (!Thread.currentThread().isInterrupted()) {
                count++;
                if (count == 1_000_000) break;
            }
            System.out.println("Stopped after " + count + " iterations");
        });
        t.join();
    }
}

joinUntilによる期限

合計実行時間に上限を設けるには、joinUntil(Instant)を使用します。すべてのサブタスクが完了する前に期限を過ぎると、TimeoutExceptionがスローされ、スコープが残りのサブタスクをシャットダウンします。

タイムアウト付き処理の形

タイムアウトで保護された処理の分散は、通常このような形になります。期限は各サブタスク個別ではなく、グループ全体に適用されます。

import java.time.*;
import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            scope.fork(() -> "fast");
            scope.joinUntil(Instant.now().plus(Duration.ofSeconds(2)));
            scope.throwIfFailed();
            System.out.println("Completed within deadline");
        } catch (TimeoutException e) {
            System.out.println("Deadline exceeded");
        } catch (ExecutionException e) {
            System.out.println("A subtask failed");
        }
    }
}

joinの後には必ず再確認する

守るべき手順は、join()(またはjoinUntil)の後に、必ずthrowIfFailed()を呼び出すか、result()を読み取ってから、サブタスクの値を信頼することです。これを省略すると、失敗を見落とす可能性があります。

クリーンなシャットダウンの保証

スコープが通常どおり終了した場合でも、失敗した場合でも、タイムアウトした場合でも、try-with-resourcesブロックを閉じることで、すべてのサブタスクのスレッドが終了することが保証されます。リークも孤立したタスクもありません。

理解度チェック

兄弟サブタスクがどのように停止されるかを思い出しましょう。

まとめ

エラー処理とキャンセルについて学びました。

  • 例外はthrowIfFailed()またはresult()で現れます
  • throwIfFailed(mapper)を使って原因を独自の例外に変換できます
  • キャンセルはスレッドの割り込みによって機能するため、協調的に動作するタスクを記述します
  • joinUntilでグループ全体の期限を設定できます
  • スコープを閉じると、必ずクリーンアップされます

これでStructured Concurrencyコースは完了です。

よくある質問

「エラー処理とキャンセル」レッスンは無料ですか?

はい。「エラー処理とキャンセル」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「エラー処理とキャンセル」で何を学びますか?

失敗を適切に伝播させます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「エラー処理とキャンセル」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 構造化並行性モデル
  2. StructuredTaskScope
  3. 失敗時と成功時のシャットダウン
  4. エラー処理とキャンセル
← Java Academyに戻る