ExampleDifficulty: Intermediate2 min

Command in the Wild: Java's Runnable and ExecutorService

A task is an object you hand to a pool, which runs it later on another thread.

Java's java.util.concurrent package is built on commands. A Runnable is an object with one method, run, and a Callable is the same with a return value. An ExecutorService is an invoker: it takes these objects, queues them, and runs each on one of its own threads. This card submits two tasks to a pool and waits for both.

java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CmExecutor {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newSingleThreadExecutor();

        Runnable sendEmail = () -> System.out.println(
                "email sent on " + Thread.currentThread().getName());
        Callable<Integer> countStock = () -> {
            System.out.println("stock counted on " + Thread.currentThread().getName());
            return 42;
        };

        System.out.println("submitting on " + Thread.currentThread().getName());
        Future<?> emailDone = pool.submit(sendEmail);
        Future<Integer> stock = pool.submit(countStock);

        emailDone.get();
        System.out.println("stock level: " + stock.get());
        pool.shutdown();
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
submitting on main
email sent on pool-1-thread-1
stock counted on pool-1-thread-1
stock level: 42
Two tasks queued from main, run in order on the pool's thread.

The tasks run on pool-1-thread-1, not on main, which shows the request travelling from the thread that created it to the one that ran it. The pool has a single thread, so the tasks run in the order submitted, and main waits on each Future, an object that will hold the task's result, before printing. Both choices make the output repeat on every run; with several threads the order of the two task lines could change.

The participants map onto the template. Runnable and Callable are the command interface, each lambda is a concrete command, the code the lambda calls is the receiver, and the ExecutorService is the invoker.

This example shows the other half of the intent. The editor's commands were kept so they could be undone; these are kept so they can wait. There is no undo here, and none is needed. What the two share is the request as an object, handed to something that decides when to run it.