0Pricing
Java Academy · Lesson

Error Handling and Cancellation

Propagate failures cleanly.

Error Handling and Cancellation is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Failures in a Tree of Tasks

When subtasks run concurrently, errors can happen in any branch. Structured concurrency gives you a disciplined way to propagate those failures up to the parent.

This lesson covers exceptions, cancellation, interruption, and timeouts.

Where Exceptions Surface

A subtask that throws does not crash your program immediately. The exception is captured on its Subtask handle. Where it surfaces depends on the policy:

  • ShutdownOnFailure: at throwIfFailed()
  • ShutdownOnSuccess: at result() if none succeeded

Propagating with throwIfFailed

Here a subtask fails. throwIfFailed() rethrows it wrapped in ExecutionException, which we catch in 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());
        }
    }
}

Mapping to a Custom Exception

throwIfFailed accepts a function to wrap the cause in your own exception type, keeping your API clean.

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());
        }
    }
}

Automatic Cancellation

When one subtask fails under ShutdownOnFailure, the scope cancels the siblings by interrupting their threads. You do not write any cancellation code yourself.

This prevents wasted work and dangling tasks.

Interruption Is the Mechanism

Cancellation works by interrupting the subtask's virtual thread. Cooperative code should:

  • Let blocking calls throw InterruptedException
  • Periodically check Thread.currentThread().isInterrupted() in long loops

Responding to Interruption

This task checks the interrupt flag in a loop so it can stop early when the scope shuts down.

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();
    }
}

Deadlines with joinUntil

To bound total time, use joinUntil(Instant). If the deadline passes before all subtasks finish, it throws TimeoutException and the scope shuts the rest down.

Timeout Example Shape

A typical timeout-guarded fan-out looks like this. The deadline applies to the whole group, not each subtask individually.

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");
        }
    }
}

Always Re-check After join

The discipline is: after join() (or joinUntil), always call throwIfFailed() or read result() before trusting any subtask value. Skipping this can hide failures.

Clean Shutdown Guaranteed

No matter how a scope ends, normally, by failure, or by timeout, the closing of the try-with-resources block guarantees every subtask thread is finished. There are no leaks and no orphans.

Quick Check

Recall how sibling subtasks are stopped.

Recap

You learned error handling and cancellation:

  • Exceptions surface at throwIfFailed() or result()
  • Map causes to custom exceptions via throwIfFailed(mapper)
  • Cancellation works through thread interruption; write cooperative tasks
  • joinUntil enforces a group deadline
  • Scope close always cleans up

That completes the Structured Concurrency course.

Frequently asked questions

Is the “Error Handling and Cancellation” lesson free?

Yes — the full text of “Error Handling and Cancellation” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Error Handling and Cancellation”?

Propagate failures cleanly. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Error Handling and Cancellation” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Structured Concurrency Model
  2. StructuredTaskScope
  3. Shutdown on Failure and Success
  4. Error Handling and Cancellation
← Back to Java Academy