River for Java

River now supports Java alongside Go, Rust, TypeScript, and Ruby. The new package is 100% pure Java, but interoperable with every other River language.

A few weeks ago we announced that River was going polyglot, supporting Rust, TypeScript, and Ruby. Today, we’ve got one more straggler to introduce: River for Java.

Java’s always been the silent workhorse, rarely in the limelight, but still one of the most frequently deployed languages in the world. I’ve known this on some level for years, and yet when I looked up stats for this post, I still couldn’t believe it!

According to the Stack Overflow Developer Survey 2026, 27.6% of respondents reported extensive Java development in the last year, putting it in the #2 position behind only TypeScript, well ahead of C#, Rust, Go, and Ruby.

Stack Overflow Developer Survey · 2026
  • TypeScript 43.8%
  • Java 27.6%
  • C# 27.1%
  • Rust 18.0%
  • Go 16.5%
  • Kotlin 10.8%
  • Ruby 5.8%
1.7× as many respondents as Go
4.8× as many respondents as Ruby
Source: Stack Overflow Developer Survey 2026 14,099 respondents. Multiple selections allowed. This is a selection of languages, not the full ranking.

That’s 1.7x as many developers as Go and almost 5x as many as Ruby!

Similarly, GitHub’s 2025 Octoverse Report (no 2026 report yet) found Java to be the #4 language on GitHub by monthly contributors, behind only TypeScript, Python, and JavaScript.

GitHub Octoverse · 2025
#4 Language by
monthly contributors
August 2025
  1. TypeScript
  2. Python
  3. JavaScript
  4. Java
Contributor growth
+20.7%
Year over year
More monthly contributors
≈175K
Year-over-year increase
New Java repositories
3.52M
Created over 12 months
Source: GitHub Octoverse 2025 Contributor growth compares August 2025 with August 2024. Repository counts cover September 2024–August 2025.

If you solely read commentary on Hacker News, you’d be forgiven for assuming that Java had disappeared as a language somewhere circa 2015, but that turns out to be far from the truth. It’s widely deployed, and even if it doesn’t quite have the same stratospheric ascendancy as TypeScript, it’s still picking up momentum.

Neat language features

Like we did for Rust, TypeScript, and Ruby, let’s take a quick look at a few things that the Java version of River does better than any of its counterparts.

Goroutines for Java

Version 21 shipped virtual threads. Before that, Java used “platform threads”, each a thin wrapper over threads from the underlying operating system. Virtual threads are green threads, created and scheduled by the JVM, and mapping many-to-one onto underlying OS threads.

Like goroutines, they’re extremely lightweight, so programs can create millions of them with no worry of resource exhaustion. No special async constructs are necessary to take advantage of concurrency: users write ordinary blocking code like anywhere else, requiring no additional cognitive overhead, and making broad refactoring trivial. River workers use virtual threads, so this applies to them:

var client = Client.builder(database)
.queue("default", 100)
.worker(fetch, context -> {
var request = HttpRequest.newBuilder(
URI.create(context.args().url())).build();
var response = http.send(
request, HttpResponse.BodyHandlers.ofString());
context.output(response.statusCode());
})
.build();

A surprise: The most concise jobs?

Java’s record types are a perfect fit for River’s job args. In conjunction with lambdas, they enable an incredibly compact definition of what gets serialized and how the job runs:

record SendEmail(String address, String subject) {}
var email = JobType.of("send_email", SendEmail.class);
var client = Client.builder(database)
.queue("default", 10)
.worker(email, context ->
mailer.send(
context.args().address(),
context.args().subject()))
.build();
client.insert(email, new SendEmail("person@example.com", "Welcome"));

In what will be a surprise to almost everyone, Java might actually be the most concise of all River’s implementation languages. Only Ruby gives it a run for its money on line count, and Java’s thorough type system makes its version of the code an order of magnitude safer at compile time.

Graceful shutdown using try-with-resources

Java’s try-with-resources automatically calls close() on objects that implement AutoCloseable when the block exits. Both River client run handles and subscriptions support it:

try (var subscription = client.subscribe(
event -> System.out.println("Completed " + event.job().id()),
Client.EventKind.JOB_COMPLETED);
var run = client.start()) {
application.awaitStop();
}

In this example we create an event subscription, then start the client. When the try block exits, resources are closed deterministically in reverse declaration order: the client stops first, then the subscription is removed.

Once again, the syntax is shockingly compact, especially for a language infamous for its verbosity. The equivalent Go code takes 4-5x as many lines.

Ready to run

All our docs’ code samples are now available in Java along with other supported languages. Take a look at getting started for install instructions for Maven and Gradle’s Kotlin DSL, plus the basics of inserting and working jobs.

Like its counterparts, River for Java is independently implemented – entirely in pure Java, with no Go required anywhere to run it. It can insert and work jobs, run its own migrations, and act as leader to maintain queues. It’s fully interoperable with Rivers written in other languages connected to the same database: jobs inserted by any of them can be worked by any other.

We’re supporting a minimum of Java 21. We thought this was a good compromise because although it’s an older LTS release (2023), it doesn’t require us to go digging too far back in ancient history.

LTS releaseReleasedEOL (Oracle)River support
Java 17September 14, 2021September 2029Unsupported
Java 21September 19, 2023September 2031Minimum supported
Java 25September 16, 2025September 2033Supported
Java 29September 21, 2027 (planned)September 2035Will be supported

Obviously, we’d love for you to try it! Head to the quickstart and email us at team@ if you run into problems or find key features to be missing.