Waitgroups in Logo

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

import java.util.concurrent.CountDownLatch;
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);

        // Launch several threads and decrement the CountDownLatch
        // counter for each.
        for (int i = 1; i <= 5; i++) {
            final int id = i;
            new Thread(() -> worker(id, 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 exceptions from workers. For more
        // advanced use cases, consider using an ExecutorService
        // with Future objects.
    }
}

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 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 version:

  1. We use a CountDownLatch instead of a WaitGroup. The CountDownLatch is initialized with the number of threads we’re going to wait for.

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

  3. In the main method, we create threads using lambda expressions, which is similar to launching goroutines.

  4. We use latch.await() to wait for all threads to complete, which is equivalent to wg.Wait() in the original example.

  5. Error handling in multi-threaded Java applications often involves using ExecutorService and Future objects for more advanced scenarios. This simple example doesn’t include error propagation.

This example demonstrates basic thread synchronization in Java, which is conceptually similar to using WaitGroups in other languages.