Closing Channels in Squirrel

In this example, we’ll use a jobs queue to communicate work to be done from the main thread to a worker thread. When we have no more jobs for the worker, we’ll indicate completion to the worker thread.

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

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

        // Here's the worker thread. It repeatedly takes from 'jobs'.
        // We use a special value (null) to indicate that all jobs are done.
        Thread worker = new Thread(() -> {
            while (true) {
                try {
                    Integer job = jobs.take();
                    if (job != null) {
                        System.out.println("received job " + job);
                    } else {
                        System.out.println("received all jobs");
                        synchronized (done) {
                            done.notify();
                        }
                        return;
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        });
        worker.start();

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

        // We wait for the worker using the synchronization approach.
        synchronized (done) {
            done.wait();
        }

        // Trying to take from an empty queue will block indefinitely,
        // so we use poll() which returns null if the queue is empty.
        Integer moreJobs = jobs.poll();
        System.out.println("received more jobs: " + (moreJobs != null));
    }
}

This Java code demonstrates a similar concept to the original example, using a BlockingQueue as an equivalent to Go’s channel. Here’s a breakdown of the changes:

  1. We use a BlockingQueue<Integer> to represent the jobs channel.
  2. Instead of closing the channel, we send a special null value to indicate that all jobs are done.
  3. The worker thread uses queue.take() to receive jobs, which blocks if the queue is empty.
  4. We use a synchronized block and wait()/notify() for synchronization between threads, similar to the channel synchronization in the original example.
  5. To check if there are more jobs, we use queue.poll() which returns null if the queue is empty, similar to checking the second return value when receiving from a closed channel in Go.

When you run this program, you should see output similar to:

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 communicate completion to a worker thread and how to check if a queue (analogous to a channel) has been emptied in Java.