Closing Channels in Logo

In Java, we don’t have built-in channels like in some other languages. However, we can simulate similar behavior using BlockingQueue from the java.util.concurrent package. We’ll use ArrayBlockingQueue for this example.

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

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

        // Here's the worker thread. It repeatedly takes from 'jobs'.
        // We use a special value (null) to indicate that no more jobs will be sent.
        Thread worker = new Thread(() -> {
            while (true) {
                try {
                    Integer j = jobs.take();
                    if (j != null) {
                        System.out.println("received job " + j);
                    } else {
                        System.out.println("received all jobs");
                        done.put(true);
                        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 no more jobs.
        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 await the worker using the synchronization approach.
        done.take();

        // After closing, any attempt to take from the queue will return null immediately.
        Integer j = jobs.poll();
        System.out.println("received more jobs: " + (j != null));
    }
}

In this example, we use ArrayBlockingQueue to simulate channels. The BlockingQueue interface provides methods that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

We create a worker thread that continuously takes jobs from the jobs queue. To signal that no more jobs will be sent, we send a null value, which the worker interprets as a signal to finish.

The main thread sends three jobs to the worker, then sends a null to indicate it’s finished. It then waits for the worker to signal completion by taking from the done queue.

After the worker has finished, we attempt to take another job from the jobs queue. Since the queue is empty (and we’re using poll() which doesn’t block), this returns null immediately.

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-like behavior in Java using BlockingQueue. While it’s not identical to channel closing in some other languages, it achieves a similar effect of signaling completion to a worker thread.