What final structured concurrency in JDK 28 means
Java's StructuredTaskScope leaves preview in JDK 28 with no API changes from JDK 27. Here is what that means for code you build and deploy.
According to InfoQ’s report, JEP 543 Finalizes Structured Concurrency in JDK 28, Ending Seven Preview Rounds, Java’s structured concurrency API (StructuredTaskScope) has been integrated into JDK 28 as a final feature. The deciding detail is that the API has no changes from the seventh preview in JDK 27. InfoQ says that if your code already runs on JDK 27, migrating comes down to recompiling without the --enable-preview flag. JDK 28 is scheduled for general availability in March 2027.
The background you need
JEP stands for JDK Enhancement Proposal. It is the design document for a change to the Java platform. A new JDK ships every six months, and each release picks up a set of JEPs.
New Java APIs often pass through trial stages before they become permanent.
- An incubator API lives in a separate, clearly experimental module.
- A preview feature is fully specified but not yet locked in. You have to opt in with
--enable-previewat both compile time and run time.
Preview code is tied to one exact JDK release. Class files compiled with preview features enabled only run on that same release, and only with the flag turned on. That is why library authors mostly avoid preview APIs. Using one would force every user onto a specific JDK and a special flag.
Project Loom is the OpenJDK effort that brought virtual threads to Java. Virtual threads are lightweight threads that the JVM manages itself, so you can run very large numbers of them cheaply. Structured concurrency is the Loom feature for coordinating those threads.
Here is the problem it solves. Most Java code that fans work out to several threads uses an ExecutorService and collects Future objects. InfoQ points out two weaknesses of that approach:
- A parent method can return while its subtasks keep running in the background.
- A subtask’s failure only shows up when someone eventually calls
Future.get().
In a running service, those weaknesses show up as leaked threads. They also show up as downstream calls that keep going after the request that needed them has already failed, and as thread dumps where nothing tells you which work belongs to which request.
Structured concurrency aims, in the words of the JEP quoted in InfoQ’s weekly roundup, to “treat groups of related tasks running in different threads as a single unit of work”. InfoQ adds two guarantees. A failure in one subtask cancels the others, and the parent scope cannot exit until all child threads have finished.
An analogy: think of a team lead who sends people out on errands. She cannot leave the office until everyone is back. If one errand fails, she calls the others back.
The analogy breaks on that last step. In Java, cancellation is cooperative and works through thread interruption. A subtask stuck in code that ignores interruption keeps running until it finishes, and the scope waits for it.
The road here was long. InfoQ lists two incubator rounds, JEP 428 in JDK 19 and JEP 437 in JDK 20. Seven previews followed, from JEP 453 in JDK 21 through JEP 533 in JDK 27. InfoQ calls this the longest preview cycle of any Project Loom feature.
What actually changed
The new part is the status. In JDK 28, StructuredTaskScope in java.util.concurrent is a standard API, and no flag is needed.
The API itself is not new. InfoQ says the finalization commit only removes the preview annotations and changes no method signatures. The shape was set in JDK 27, when JEP 533 added a third type parameter. Because of that parameter, join() declares the specific exception its completion policy can throw, instead of a preview-only FailedException.
In practice, you open a scope with StructuredTaskScope.open() inside a try-with-resources block, then:
- fork subtasks, each on a virtual thread by default;
- call
join()once, to wait for the outcome; - let
close()run at the end of the block, which cancels unfinished subtasks and waits for their threads to end.
Completion policies are objects called joiners. With the default policy, join() waits for every subtask to succeed, cancels the rest on the first failure, and throws ExecutionException. According to InfoQ, the built-in throwing joiners also throw ExecutionException, the same wrapper Future.get() has long used. A custom joiner can declare its own checked exception instead. Other built-in joiners do things like:
- return the first success and cancel the rest;
- cancel the scope once a condition you supply matches.
The two InfoQ pieces describe different stages of the process. The roundup, which covers the week of October 12th, 2026, says JEP 543 was moved from Proposed to Target to Targeted for JDK 28. The main report says the work has been integrated. Targeting normally comes before integration, so the two need not conflict. Neither source gives an integration date.
Teams coming from the JDK 26 preview face real code changes. InfoQ lists these:
FailedExceptionis replaced byExecutionException.- Custom joiner signatures gain the new type parameter.
StructuredTaskScope.TimeoutExceptionis gone. Timeouts are now reported through aCancelledByTimeoutExceptioncause.Joiner.awaitAll()is no longer available.onTimeout()is replaced by an abstracttimeout()method.
What it means for developers who deploy and run apps
If you use the preview on JDK 27, this is a build change, not a code change. Remove --enable-preview everywhere it appears: javac options, test runner settings and launch scripts. In practice that also means Dockerfiles, CI pipelines, and environment variables that inject JVM options. InfoQ says to recompile on a JDK 28 early-access build that includes the change.
If you use the JDK 26 preview, budget time for the API changes listed above, especially around timeouts and custom joiners.
If you never touched it, the change that matters most to you probably arrives through your dependencies. InfoQ calls the bigger shift the one for library and framework maintainers, who can now build on structured concurrency without forcing preview flags on users. Watch the release notes of the web frameworks and clients you rely on.
A few details matter once this runs in production:
- Observability. You can give a scope a name. Subtasks appear under their parent in JSON thread dumps, which makes stuck requests easier to trace.
- Context. Subtasks inherit
ScopedValuebindings, the newer way to pass request context across threads. - Deadlines. A scope can take a timeout that cancels it if it expires before or during
join(). - Fail-fast misuse. Mistakes like closing scopes out of order throw
StructureViolationExceptioninstead of leaking threads.
If you are staying on JDK 25 for now, InfoQ notes that the velo/structured-concurrency project backports the JEP 543 API to JDK 25. It also ships recipes for moving to the JDK classes after you upgrade.
What we don’t know yet
- Which failure you will see. InfoQ reports that when several subtasks fail at once, the API does not say which failure
join()reports. Viktor Klang raised the question during code review. Alan Bateman, the JEP’s owner at Oracle, reworded the Javadoc so it promises only that the exception comes from a failed subtask. Do not write error handling that depends on a particular one. - Which early-access build to test. InfoQ says to use a JDK 28 early-access build that includes the change, but does not name one. The roundup mentions Build 20 without saying whether it contains JEP 543.
- The exact release date. March 2027 is a schedule, not a shipped release.
- Framework timelines. The sources say maintainers can now adopt the API. They do not say who will, or when.
FAQ
Do I have to rewrite my ExecutorService code for JDK 28?
I use the preview on JDK 27. What do I actually change?
Does structured concurrency require virtual threads?
Related tools
Beehiiv
Newsletter platform with built-in ad network and Boost referrals.
Try Beehiiv →
Webflow
Visual site builder with real CSS export and a CMS that scales.
Try Webflow →
Some links above are affiliate links. We may earn a commission if you sign up. See our disclosure for details.
Related reading
2026-10-17
What Amazon buying DuckDB's makers means for Aurora users
AWS bought DuckLabs and put DuckDB inside Aurora PostgreSQL. What you can now query, what it costs, and what stays open source.
2026-09-02
OpenAI Deployment Layer: The Assistants API Precedent
OpenAI shipped the Assistants API in November 2023 and marked it for sunset 16 months later. That precedent is how to price the new deployment stack.
2026-08-21
701 Bad Internal Links Before 49 Good Ones
A phrase-matching script inserted 750 links across 269 MDX articles. Here is where they failed, and how claim-matching cut the review pile to 128.
2026-08-21
Cloudflare Web Analytics via GraphQL: the siteTag Filter
Why accountTag and siteTag differ in rumPageloadEventsAdaptiveGroups, how the limit argument truncates silently, and which fields we did not verify.
2026-08-21
Bing Webmaster API's 100-URL Cap: 289 URLs, Three Days
Quota is 100 a day against 1300 a month, GetQueryStats was still empty at the end, and two error shapes will kill a cron job.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.