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.