Jakarta Concurrency: Present and Future
Originally published on 27 Oct 2021
Last updated on 01 Aug 2024
Jakarta EE, previously Java EE, is a set of specifications that enables the world wide community of Java developers to work on cloud native Java enterprise applications. It is an open source project maintained by theEclipse Foundation.
Jakarta Concurrency is a small, but fundamental, specification under the Jakarta EE umbrella. As project lead, I provide more information on what it is, its future and how to be involved.
Why Do We Need Concurrency?
When programming in Java, you need to take into consideration the level of context for your application when you move between different threads. For example, if you’ve logged into a REST API and then need to create a task on a new thread, you need to retain the security context. To do this in Java SE, you use concurrency primitives: units of code related to concurrency, multithreading, and parallelism.
If you spawn a new thread in Java SE, the Jakarta EE runtime is unaware of the thread, and would struggle to establish its security (or classloader, or CDI, etc.) context.
This is where Jakarta Concurrency comes in. It updates Java SE concurrency primitives for use in a Jakarta EE environment. Java SE Executor Service becomes Managed Executor Service, for example, which has the same API. These analogous concurrency primitives allow you to use Java SE concurrency measures in your application server.
Jakarta EE concurrency provides consistency between the Java SE and Jakarta EE platforms, for a simple migration path from SE to EE. It also allows you to easily design new Jakarta EE applications using concurrency design principles, and add concurrency to existing applications in a Jakarta EE application server.
Jakarta EE aims to allow developers to concentrate on business logic code, taking away infrastructural and operational tasks. One of these tasks is thread management. Jakarta Concurrency, therefore, allows you to access these in a managed Jakarta EE runtime.
Use Case 1: Adding an Asynchronous Task in Your Application
Before Jakarta Concurrency, a Java EE developer would need to use JMS to build an asynchronous task into the application. A HTTP request coming in to a servlet or REST endpoint, and needing a long-running action in response, would require packaging the request info and pushing it into a JMS queue using a message driven bean. This is a heavyweight process.
Jakarta Concurrency makes this much simpler. If you have a HTTP request, you can use the same component you would in Java SE, but in a Java EE version: Managed Executor Service. You’d submit a job to this, and this would run in a managed thread to replace your long-running action. This thread will be managed by the Jakarta runtime, and apply the same context in the REST request to the action. It’s therefore much more lightweight.
In code form, this would look like this, providing a vastly more simple code than using JMS:
@Path("concurrency") @RequestScoped public class GenericResource { @Context private UriInfo uriInfo; @Resource private ManagedExecutorService managedExecutor; @GET @Path("simpleJob") @Produces(MediaType.TEXT_PLAIN) public String getText() { managedExecutor.submit(() -> { System.out.println("Job running"); }); return "Job Submitted"; } }
Use Case 2: Running Tasks in Parallel
You may have a REST request coming in, and want to run two tasks in parallel, merge the result and return it to a user. With Jakarta Concurrency you can inject the Managed Executor Service into your REST endpoint, and then you can use the Managed Executor API to submit two jobs at once. The method returns immediately and you get back an instance of Future class. You can get the outcome of the job by calling the get() method and merge it and then return to the user.
Previously, this would have been incredibly difficult to do, needing JMS, with a need to create correlation IDs. With Jakarta Concurrency, the resulting code would look something like this:
@GET @Path("parallelJob") @Produces(MediaType.TEXT_PLAIN) public String getParallelJob() throws ExecutionException, InterruptedException { Future future1 = managedExecutor.submit(() -> { System.out.println("Job 1 running ..."); // This takes some while System.out.println("Job 1 finished ..."); }); Future future2 = managedExecutor.submit(() -> { System.out.println("Job 2 running ..."); // This takes some while System.out.println("Job 2 finished ..."); }); future1.get(); // Wait for job to finish and get result (optionally) future2.get(); return "Jobs completed"; }
What Are The Main Components of Jakarta Concurrency?
-
Managed Executor Services
Managed Executor Service in Jakarta EE maps on to Executor Service in Java SE, Managed Scheduled Executor Service maps on to Scheduled Executive Service (as you have seen in the use cases above). The API is the same, but the task is run in the context required for Jakarta EE.
-
Managed Thread Factory
Managed Thread Factory in Jakarta EE maps on to Thread Factory in Java SE. This can be used when you have an API that creates its own threads but has no knowledge of Jakarta EE. You can pass in a Thread Factory. This allows you to call into APIs or libraries that are not aware of Jakarta Concurrency but do take a Thread Factory as a parameter. With Managed Thread Factory, when a task is created to run on the thread, it sets up the correct context again.
-
Context Service
Context Service enables you to wrap your Runnable, creating a contextual proxy to submit to any raw thread. It will establish all the context you expect, and therefore is useful for if you are using an API or library that has no knowledge of Jakarta EE but is spawning threads.
Future Outlook of Jakarta Concurrency
Jakarta Concurrency arrived in Java EE 7, in 2013. As with other specifications, it was moved into the Jakarta EE namespace with the Jakarta EE 9 release, and made compatible with Java SE 11 within Jakarta EE 9.1. Now the transition is complete, it is in the place to start making substantial, functional changes to specifications.
Some of the ideas in development (available to view inGitHub) are:
-
Deployable Managed Objects
Currently all Managed Objects (other than the default ones) must be created by the application server administrator.
Jakarta EE 9 has deployable application scoped data sources. What is suggested here is a feature whereby you could set up your own application scoped and configured managed executor services. These would be deployed using annotations.
If you have an application which really needs to have fine-grained control of the threading, concurrency and pooling of threads, this feature will allow you to set these processes up without needing administration consoles.
-
New @Asynchronous annotation
Currently there are annotations in Jakarta EE to indicate asynchronous execution, but they are quite specific to each individual specification. This suggested new annotation could be used with CDI Beans. Adding and being able to configure executor service pools will enable fine grained concurrency management when combined with deployable executor services. You could choose different thread pools for different methods, for example.
This new feature could eventually result in a single @Asynchronous annotation common to all the specifications. This would simplify the platform and make it more unified.
-
Catch up with java.until.concurrent
Jakarta Concurrency was released in Java EE 7, so has not been brought up to date to match concurrency primitives brought in with Java SE 9, 11 and 17. Some of these include support for the ForkJoinPool in a standard way, and updated APIs for current Java SE Managed Executor Services.
Get Involved!
As the project lead of Jakarta Concurrency, my goal is not to define its future, but lead a community group that can drive the specification forwards. In short, I need your help to make the specification happen!
Each specification needs to produce a specification document, detailing how you use the API; an API JAR, to be coded against as a developer; and a Technology Compatibility Kit (TCK), the test suite that is used to determine whether independent implementations of the API meet its requirements.
You can get involved in each and every part of the process. For example, if you are interested in testing, you could help build tests or maintain the TCK. You can also help directly on the API, working out what capabilities you’d want as a developer. There is also help needed around the specification document, from submitting PRs for typos to higher level input.
The whole project is on GitHub, you can make your API changes there:
Find the Eclipse Project here:
Join the Mailing List:
There is also a compatible implementation on GitHub, used in GlassFish and Payara:
It can seem daunting to become involved with an open source project, but Jakarta Concurrency is actively asking for help of all kinds. If in doubt - get involved!
Related Posts
Moving Beyond GlassFish - Here's Why and How
Published on 11 Nov 2024
by Chiara Civardi
0 Comments
If you’re still managing Java applications on GlassFish middleware, you might be hitting some roadblocks, such as difficulties with automation, lack of integrated monitoring and official support and limited abilities with modern tools. However, ...
Nugget Friday - Building Resilient Microservices with MicroProfile Fault Tolerance
Published on 08 Nov 2024
by Luqman Saeed
0 Comments
While we all want maximum uptime for our software systems, failures and downtimes are inevitable. However, these can be minimized and quickly resolved through comprehensive, robust and well-designed fault tolerance mechanisms. This Nugget ...