Feedback about structured Concurrency and proposing of a new functional interface akin runnable

david Grajales david.1993grajales at gmail.com
Thu Sep 4 03:15:33 UTC 2025


I hope this message finds you well. Thank you for all the work you are
doing to improve the Java platform.

I wanted to share some thoughts from a small experiment I’ve been running
with structured concurrency. The idea was to mimic (to some extent) Go’s
goroutines and channels, modeling an N:M producer–consumer fan-out using
StructuredTaskScope together with ArrayBlockingQueue as a “channel.” The
intention is to build groups of task pipelines step by step that can be
executed in parallel.

Here’s a simplified snippet that captures the idea:

void main(){
    var producerChannel = new ArrayBlockingQueue<HttpRequest>(100);
    var consumerChannel = new ArrayBlockingQueue<HttpResponse<String>>(3);
    var client = HttpClient.newBuilder().build();
    var request = HttpRequest.newBuilder()
        .uri(URI.create("some url"))
        .build();
    try(var routine = StructuredTaskScope.open()){
        for(var i = 0; i < 100; i++){
            routine.fork(() -> sendRequest(producerChannel,
consumerChannel, client));
            producerChannel.put(request);
        }

        routine.fork(() -> processResponse(consumerChannel));
        routine.join();

    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

}

Void sendRequest(ArrayBlockingQueue<HttpRequest> producerChannel,
                          ArrayBlockingQueue<HttpResponse<String>>
consumerChannel,
                          HttpClient client) throws InterruptedException,
IOException {
    var req = producerChannel.take();
    var res = client.send(req, HttpResponse.BodyHandlers.ofString());
    consumerChannel.put(res);
    return null;
}

Void processResponse(ArrayBlockingQueue<HttpResponse<String>>
consumerChannel) throws InterruptedException {
    for (var i = 0; i < 100; i++){
        var value = consumerChannel.take().body();
        println(value);
    }
    var value = consumerChannel.take().body();
    println(value);
    return null;
}

One thing I noticed is that having to declare Void and return null
introduces a bit of noise. This ceremony is required because Runnable
doesn’t allow checked exceptions in its contract (and never will, for
backwards compatibility). Yet, in practice, many real-world tasks don’t
return values directly — instead, they write to or consume from buffers.

This makes the Void return type and return null feel redundant. I was
wondering: would it be worth considering the addition of a Runnable-like
functional interface that permits checked exceptions?

Thank you for your time and consideration. I’d love to hear your thoughts.

Best regards and always yours.

David Grajales Cárdenas.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20250903/20f8c7a0/attachment.htm>


More information about the loom-dev mailing list