Structured concurrency for the JVM without the magic.
Why does concurrency in Java have to be so hard?
I asked this in my original announcement post.
Now I’m excited to say:
JCoroutines 0.1.0 Released 🎉
JCoroutines is now released and available on Maven Central!
Maven coordinates:
<dependency>
<groupId>tech.robd</groupId>
<artifactId>jcoroutines</artifactId>
<version>0.1.0</version>
</dependency>
About JCoroutines
JCoroutines is a lightweight concurrency toolkit for Java 21+, inspired by Kotlin coroutines.
It provides structured concurrency with scopes, cancellation, timeouts, and channels, built on Java’s new virtual threads.
The goal: make concurrency feel clean and explicit—without needing a heavy reactive framework.
Key features
- Structured task scopes
- Fail-fast cancellation (siblings cancel together)
- Timeout handling
- Channel support with backpressure
- Virtual-thread friendly execution
Version History
- 0.1.0 – Initial release
- Core
JCoroutineScopeAPI - Cancellation and timeout support
- Channels with backpressure
- Full JUnit 5 test suite
- Core
(Version history will be expanded here with each release.)
Links
Getting Started with JCoroutines
Requirements
- Java 21+ (uses Virtual Threads)
- No additional runtime dependencies
Installation (Gradle)
dependencies {
implementation("tech.robd:jcoroutines:0.1.0")
}
Installation (Maven)
<dependency>
<groupId>tech.robd</groupId>
<artifactId>jcoroutines</artifactId>
<version>0.1.0</version>
</dependency>
Quick Start Examples
Basic async
import tech.robd.jcoroutines.*;
JCoroutineHandle<String> handle = Coroutines.async(suspend -> {
suspend.delay(100);
return "Hello, Coroutines!";
});
System.out.println(handle.join());
Structured concurrency
try (var scope = new StandardCoroutineScope()) {
var h1 = scope.async(suspend -> fetchData(suspend, "url1"));
var h2 = scope.async(suspend -> fetchData(suspend, "url2"));
System.out.println(h1.join());
System.out.println(h2.join());
} // automatic cleanup & cancellation
Channels
try (var scope = new StandardCoroutineScope()) {
var channel = InteropChannel.<String>buffered(10);
scope.launch(suspend -> {
for (int i = 0; i < 5; i++) {
channel.send(suspend, "Message " + i);
suspend.yieldAndPause(Duration.ofMillis(100));
}
channel.close();
});
scope.launch(suspend ->
channel.forEach(suspend, (ctx, msg) ->
System.out.println("Received: " + msg.orElse("null"))
)
);
}
Key Concepts
- Suspend Functions – always take a
SuspendContext - Cooperative Scheduling –
yield(),yieldAndPause(),delay() - Cancellation – explicit checks via
suspend.checkCancellation() - Resource Management – use try-with-resources for scopes
