Closing Channels in Karel

import java.util.concurrent.*;

public class ClosingChannels {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> jobs = new LinkedBlockingQueue<>(5);
        CountDownLatch done = new CountDownLatch(1);

        // Here's the worker thread. It repeatedly takes from 'jobs'.
        // We use the special 'poll' method with a timeout to check if the queue is empty and no more items will be added.
        // We use this to notify on 'done' when we've worked all our jobs.
        new Thread(() -> {
            while (true) {
                try {
                    Integer j = jobs.poll(100, TimeUnit.MILLISECONDS);
                    if (j != null) {
                        System.out.println("received job " + j);
                    } else {
                        System.out.println("received all jobs");
                        done.countDown();
                        return;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // This sends 3 jobs to the worker over the 'jobs' queue.
        for (int j = 1; j <= 3; j++) {
            jobs.put(j);
            System.out.println("sent job " + j);
        }
        System.out.println("sent all jobs");

        // We await the worker using the CountDownLatch.
        done.await();

        // Checking if the queue is empty
        boolean isEmpty = jobs.isEmpty();
        System.out.println("received more jobs: " + !isEmpty);
    }
}

In this example, we use a BlockingQueue to communicate work to be done from the main() thread to a worker thread. When we have no more jobs for the worker, the queue will become empty.

The BlockingQueue is used as an equivalent to Go’s channel. We use LinkedBlockingQueue with a capacity of 5, similar to the buffered channel in the Go example.

Instead of closing the channel, which is not directly possible with Java’s BlockingQueue, we use a timeout mechanism in the worker thread to detect when no more jobs are coming.

The CountDownLatch is used for synchronization between the main thread and the worker thread, similar to the done channel in the Go example.

In the worker thread, we use queue.poll(timeout, unit) method to try to take an item from the queue with a timeout. If it returns null, we assume the queue is empty and all jobs have been processed.

In the main thread, we send 3 jobs to the worker over the jobs queue.

Finally, we check if the queue is empty to simulate the behavior of reading from a closed channel in Go.

To run this program:

$ javac ClosingChannels.java
$ java ClosingChannels
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false

This example demonstrates how to simulate channel closing and reading from closed channels in Java using BlockingQueue and other concurrent utilities.