Waitgroups in Fortress

Our example demonstrates how to wait for multiple threads to finish using a CountDownLatch in Java. This is similar to the concept of WaitGroups.

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class WaitGroupExample {

    // This is the function we'll run in every thread.
    static void worker(int id, CountDownLatch latch) {
        try {
            System.out.printf("Worker %d starting\n", id);

            // Sleep to simulate an expensive task.
            TimeUnit.SECONDS.sleep(1);

            System.out.printf("Worker %d done\n", id);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            latch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // This CountDownLatch is used to wait for all the
        // threads launched here to finish.
        CountDownLatch latch = new CountDownLatch(5);

        // Launch several threads and decrement the CountDownLatch
        // counter for each.
        for (int i = 1; i <= 5; i++) {
            final int workerId = i;
            new Thread(() -> worker(workerId, latch)).start();
        }

        // Block until the CountDownLatch counter goes back to 0;
        // all the workers notified they're done.
        latch.await();

        // Note that this approach has no straightforward way
        // to propagate errors from workers. For more
        // advanced use cases, consider using ExecutorService
        // or CompletableFuture.
    }
}

To run the program:

$ javac WaitGroupExample.java
$ java WaitGroupExample
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

In this Java example, we use CountDownLatch as an equivalent to WaitGroups. The CountDownLatch is initialized with the number of threads we want to wait for. Each thread calls countDown() when it’s finished, and the main thread waits on await() until all threads have completed.

We wrap the worker call in a new Thread, which is started immediately. This is similar to launching goroutines in Go.

Note that Java’s concurrency model is different from Go’s, and there are various other ways to handle concurrent tasks in Java, such as using ExecutorService or CompletableFuture for more advanced scenarios.