1. Introduction
The groovy-concurrent-java module provides Groovy’s concurrent and
parallel API as a standalone Java library — no Groovy runtime required.
Java developers get access to:
-
Structured concurrency —
AsyncScope,Awaitable -
Pools —
Pool,ParallelScope,ConcurrentConfig -
Actors —
Actor.reactor(),Actor.stateful() -
Agents —
Agent(thread-safe mutable state) -
Dataflow —
DataflowVariable,AsyncChannel,BroadcastChannel,ChannelSelect
This module is published as org.apache.groovy:groovy-concurrent-java
and contains a filtered subset of the classes from Groovy core. It must
not be used alongside the full Groovy runtime — Gradle enforces mutual
exclusion via a shared capability, and a runtime warning is logged if
both jars are detected on the classpath.
2. Setup
2.1. Gradle
dependencies {
implementation 'org.apache.groovy:groovy-concurrent-java:6.0.0'
}
2.2. Maven
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-concurrent-java</artifactId>
<version>6.0.0</version>
</dependency>
Do not add groovy-concurrent-java if you already depend on
groovy (the full runtime). The concurrent classes are included in core.
|
3. Structured Concurrency
import groovy.concurrent.AsyncScope;
import groovy.concurrent.Pool;
import org.apache.groovy.runtime.async.AsyncSupport;
// Basic scope — waits for all children before returning
var result = AsyncScope.withScope(scope -> {
var a = scope.async(() -> fetchUser(id));
var b = scope.async(() -> fetchOrders(id));
return Map.of(
"user", AsyncSupport.await(a),
"orders", AsyncSupport.await(b)
);
});
// With a pool for executor isolation
try (var pool = Pool.cpu()) {
AsyncScope.withScope(pool, scope -> {
var task = scope.async(() -> computeResult());
return AsyncSupport.await(task);
});
}
4. Actors
import groovy.concurrent.Actor;
import org.apache.groovy.runtime.async.AsyncSupport;
// Reactor — stateless, each message produces a reply
var doubler = Actor.<Integer, Integer>reactor(n -> n * 2);
int result = AsyncSupport.await(doubler.sendAndGet(21)); // 42
doubler.stop();
// Stateful — maintains state across messages
var counter = Actor.<String, Integer>stateful(0, (state, msg) -> {
if ("increment".equals(msg)) return state + 1;
return state;
});
counter.send("increment");
counter.send("increment");
int count = AsyncSupport.await(counter.sendAndGet("increment")); // 3
counter.stop();
5. Agents
import groovy.concurrent.Agent;
import org.apache.groovy.runtime.async.AsyncSupport;
var counter = Agent.create(0);
counter.send(n -> n + 1);
counter.send(n -> n + 1);
counter.send(n -> n + 1);
int result = AsyncSupport.await(counter.getAsync()); // 3
counter.shutdown();
6. Dataflow Variables
import groovy.concurrent.Awaitable;
import groovy.concurrent.DataflowVariable;
import org.apache.groovy.runtime.async.AsyncSupport;
var x = new DataflowVariable<Integer>();
var y = new DataflowVariable<Integer>();
// z depends on x and y — blocks until both are bound
var z = Awaitable.<Integer>go(() ->
AsyncSupport.await(x) + AsyncSupport.await(y)
);
// Bind in any order
AsyncSupport.getExecutor().execute(() -> x.bind(10));
AsyncSupport.getExecutor().execute(() -> y.bind(5));
int result = AsyncSupport.await(z); // 15
7. Pools
import groovy.concurrent.ParallelScope;
import groovy.concurrent.Pool;
import org.apache.groovy.runtime.async.AsyncSupport;
var result = ParallelScope.withPool(4, scope -> {
var tasks = new java.util.ArrayList<groovy.concurrent.Awaitable<Integer>>();
for (int i = 1; i <= 4; i++) {
int n = i;
tasks.add(scope.async(() -> n * 10));
}
int sum = 0;
for (var task : tasks) {
sum += (int) AsyncSupport.await(task);
}
return sum;
}); // 100
8. Features not available in the Java module
The following features require the Groovy runtime and are only
available when using the full groovy dependency:
| Feature | Reason |
|---|---|
|
Uses Groovy’s |
|
Groovy language syntax, transformed by the Groovy compiler |
|
Groovy language syntax |
|
Groovy compiler-generated iteration |
|
Groovy AST transformation |
Parallel collection methods ( |
Registered as Groovy extension methods on |
|
Return |
Closure-based APIs (e.g., |
Groovy SAM coercion from Closure to |
Java users have full access to the underlying APIs using
java.util.function types (Supplier, Function, Consumer,
Predicate, etc.) and explicit AsyncSupport.await() calls
instead of the await keyword.