Waitgroups in Squirrel

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

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

public class WaitGroups {

    // This is the method we'll run in every thread.
    private 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);

        ExecutorService executor = Executors.newFixedThreadPool(5);

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

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

        executor.shutdown();

        // Note that this approach has no straightforward way
        // to propagate exceptions from workers. For more
        // advanced use cases, consider using CompletableFuture
        // or other concurrency utilities in Java.
    }
}

To run the program:

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

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

In this Java version:

  1. We use CountDownLatch instead of WaitGroup. It serves a similar purpose of allowing one thread to wait for multiple other threads.

  2. Instead of goroutines, we use Java’s ExecutorService to manage a pool of threads.

  3. The worker method now takes a CountDownLatch as a parameter and calls countDown() when it’s done.

  4. We use latch.await() to block until all workers have finished, similar to wg.Wait() in the original example.

  5. We need to explicitly shut down the ExecutorService at the end.

  6. Error handling is different in Java. We catch and handle InterruptedException in the worker method.

This approach demonstrates how to achieve similar functionality to WaitGroups in Java, although the specific mechanisms and idioms differ between the two languages.