Jakarta Concurrency is a small, but fundamental, specification under the Jakarta EE umbrella. It provides consistency between the Java SE and Jakarta EE platforms so there’s a simple migration path from Java SE to Jakarta 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.
Why You Need Jakarta Concurrency
You need Jakarta Concurrency when programming in Jakarta EE because you have to consider the context information for your application when you move between threads. For example, if you’ve logged into a REST API, then need to create a task on a new thread, you have to retain the security context. To do this in Java SE, you use concurrency primitives: units of code related to concurrency, multithreading, and parallelism.
However, if you spawn a new thread in Java SE, the Jakarta EE runtime is not aware of it, and is unable to establish its security — or classloader, or contexts and dependency injection (CDI), or other — context.
This is where Jakarta Concurrency comes in. It builds upon Java SE concurrency primitives so they can be used in a Jakarta EE environment. For example, the Java SE Executor Service becomes the Jakarta EE Managed Executor Service, which has the same API. These analogous concurrency primitives allow you to use Java SE concurrency components in your Jakarta EE application server.
Jakarta Concurrency Use Case 1: Adding an Asynchronous Task to Your Application
Before Jakarta Concurrency, a Java EE developer had to use Java Message Service (JMS) to build an asynchronous task into the application. If an HTTP request coming into a servlet or a REST endpoint needed a long-running action in response, the request information had to be packaged and pushed into a JMS queue using a message-driven bean. This is a heavyweight process.
Jakarta Concurrency makes this much simpler. If you have an HTTP request, you can use the same component you would in Java SE, but in a Jakarta EE version: the Managed Executor Service. You submit a job to it and it runs in a managed thread to replace your long-running action. The thread is managed by the Jakarta EE runtime and the context used in the REST request is applied to the action. As a result, it’s much more lightweight.
The code to do this is shown below. It’s much simpler than the code required when JMS is used.
Jakarta Concurrency Use Case 2: Running Tasks in Parallel
You may have cases where a REST request comes in, you want to run two tasks in parallel, then merge the result, and return it to a user. With Jakarta Concurrency, you can inject the Managed Executor Service into your REST endpoint, then use the Managed Executor API to submit two jobs at once. The method returns immediately and you get back an instance of a Future class. You can get the outcome of the job by calling the get()method, merging it, then returning it to the user.
Previously, this would have been incredibly difficult to do, requiring JMS and the need to create correlation IDs.
With Jakarta Concurrency, the resulting code looks similar to the code below.
The Main Components in Jakarta Concurrency
To help you understand how Jakarta Concurrency works, here are brief descriptions of the functionality the main components provide:
Managed Executor Services
The Managed Executor Service in Jakarta EE maps to Executor Service in Java SE. Similarly, the Managed Scheduled Executor Service in Jakarta EE maps to the Scheduled Executive Service in Java SE, as shown 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
The Managed Thread Factory in Jakarta EE maps to the 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 Thread Factory as a parameter. When a task is created to run on the thread, the Managed Thread Factory sets up the correct context again.
Context Service
The Context Service allows you to wrap your Runnable, creating a contextual proxy to submit to any raw thread. It establishes all of the context you expect, so is useful if you’re using an API or library that has no knowledge of Jakarta EE but is spawning threads.
Future Outlook for Jakarta Concurrency
The Jakarta Concurrency specification was first released in 2013 in Java EE 7. In Jakarta EE 9, it was updated to use the Jakarta EE namespace, and in Jakarta EE 9.1, it was made compatible with Java SE 11. The next step is to start making substantial, functional changes to the specification.
Some of the ideas in development, which you can see in GitHub, include:
Deployable managed objects
Currently, all managed objects, other than the default objects, must be created by the application server administrator.
Jakarta EE 9 supports deployable application-scoped data sources. The suggestion is to allow developers to set up their own application-scoped data source and configured Managed Executor Services. These would be deployed using annotations.
If you have an application that requires fine-grained control of threading, concurrency, and pooling of threads, this feature would allow you to set up these processes without administration consoles.
New @Asynchronous annotation
Currently, there are Jakarta EE annotations to indicate asynchronous execution, but they are unique to each specification. The suggested new annotation could be used with CDI beans.
The ability to specify in the Asynchronous annotation an executor service pool to use enables 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 that’s common to all Jakarta EE specifications. This would simplify the platform and make it more unified.
Catch up with java.until.concurrent
Jakarta Concurrency has not yet been updated to align with concurrency primitives added in Java SE 9 and 11. These primitives include standardized support for ForkJoinPool and updated APIs for current Java SE Managed Executor Services.
Get Involved in Jakarta Concurrency
As the project lead of Jakarta Concurrency, my role is not to define its future, but to lead a community that can drive the specification forwards. In short, I need your help to make the specification happen!
Each specification team must produce:
- A specification document detailing how to use the API
- An API JAR developers can code against
- A Technology Compatibility Kit (TCK) that defines the test suite 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’re interested in testing, you could help build tests or maintain the TCK. You can also help with the API, determining which capabilities you’d want as a developer. We also need help updating the specification document in areas ranging from submitting pull requests for typos to higher level input.
To get involved, follow the links below: