Waitgroups in Karel

Here’s the translated code and explanation in Markdown format suitable for Hugo:

Our example demonstrates the use of a CountDownLatch, which is similar to a wait group in other languages. It allows one or more threads to wait until a set of operations being performed in other threads completes.

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

public class CountDownLatchExample {

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

        try {
            // Sleep to simulate an expensive task.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.printf("Worker %d done\n", id);
        latch.countDown();
    }

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

        // Create a fixed thread pool
        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.execute(() -> worker(workerId, latch));
        }

        try {
            // Block until the CountDownLatch counter goes back to 0;
            // all the workers notified they're done.
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Shutdown the executor
        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 concurrent utilities.
    }
}

To run the program:

$ javac CountDownLatchExample.java
$ java CountDownLatchExample
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 3 done
Worker 1 done
Worker 2 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 example, we use a CountDownLatch instead of a WaitGroup. The CountDownLatch is initialized with a count of 5, corresponding to the number of worker threads we’ll create.

Each worker thread calls countDown() on the latch when it completes its work. The main thread waits for all workers to finish by calling await() on the latch.

We use an ExecutorService to manage our thread pool, which is a more idiomatic way to handle concurrent tasks in Java compared to manually creating threads.

Note that unlike some other languages, Java doesn’t have built-in lightweight concurrency primitives like goroutines. Instead, we use threads, which are more heavyweight but serve a similar purpose in this context.