Navigating the Waves of Concurrency: Exploring Jakarta Concurrency
This tutorial explores Jakarta Concurrency through practical code examples, demonstrating its key features and best practices.
Join the DZone community and get the full member experience.
Join For FreeIn the vast ocean of software development, concurrency stands as a formidable wave to be mastered. With the ever-increasing demand for high-performance, scalable applications, understanding and effectively implementing concurrency becomes crucial. Among the many tools and frameworks available, Jakarta Concurrency offers developers a robust set of tools to navigate the complexities of concurrent programming in Java.
In a concurrent world, imperative is the wrong default!
Understanding Concurrency
I’m not saying writing concurrent code is hard, but even the ‘Hello, World!’ program deadlocks sometimes.
Concurrency, simply put, is the ability of a system to execute multiple tasks concurrently. In the realm of software development, this often means having multiple threads of execution running simultaneously. While this can lead to significant performance improvements and better resource utilization, it also introduces challenges such as race conditions, deadlocks, and thread synchronization issues.
Jakarta Concurrency
Jakarta Concurrency, formerly known as Java EE Concurrency Utilities, is a framework that provides high-level concurrency abstractions and utilities for Java applications. Part of the larger Jakarta EE ecosystem, Jakarta Concurrency offers developers a rich set of tools to tackle complex concurrency scenarios with ease.
Key Features and Components
Before diving into code examples, let’s first ensure we have the necessary dependencies set up. Jakarta Concurrency is part of the Jakarta EE ecosystem, so make sure you have a compatible Jakarta EE runtime or application server installed. Additionally, ensure that you have the Jakarta Concurrency API included in your project dependencies.
<dependency>
<groupId>jakarta.enterprise.concurrent</groupId>
<artifactId>jakarta.enterprise.concurrent-api</artifactId>
<version>{version}</version>
<scope>provided</scope>
</dependency>
Managed Executors
One of the core components of Jakarta Concurrency is its managed executor service. This allows developers to offload tasks to a pool of worker threads, providing automatic management of thread lifecycle and resource allocation. Managed executors simplify the process of parallelizing tasks and help optimize resource utilization.
Let’s see how we can use a managed executor to execute a task asynchronously.
..
@Resource
private ManagedExecutorService managedExecutorService;
@GET
@Path("managedExecuters")
@Produces(MediaType.TEXT_PLAIN)
public String getmanagedExecuters() throws InterruptedException, ExecutionException {
Future future1 = managedExecutorService.submit(() -> {
System.out.println("Job 1 running ...");
// This takes some while
System.out.println("Job 1 finished ...");
});
Future future2 = managedExecutorService.submit(() -> {
System.out.println("Job 2 running ...");
// This takes some while
System.out.println("Job 2 finished ...");
});
future1.get();
future2.get();
System.out.println("Jobs completed");
return "Jobs completed";
}
..
The output of the above program in the console:
[INFO] Job 1 running ...
[INFO] Job 2 running ...
[INFO] Job 1 finished ...
[INFO] Job 2 finished ...
[INFO] Jobs completed
Context Propagation
Another essential feature of Jakarta Concurrency is its support for context propagation. This allows contextual information, such as security context or transaction context, to be automatically propagated across asynchronous execution boundaries. This ensures consistency and integrity, especially in distributed systems where context management can be challenging.
..
@Resource
ContextService contextService;
@Resource
ManagedExecutorService managedExecutorService;
@GET
@Path("contextPropagation")
@Produces(MediaType.TEXT_PLAIN)
public String getContextPropagation() throws InterruptedException, ExecutionException {
//Execution 1
managedExecutorService.execute(() -> {
try {
System.out.println(new InitialContext().lookup("java:comp/env/replySuccess"));
} catch (NamingException namingException) {
namingException.printStackTrace();
}
});
ExecutorService executor = Executors.newFixedThreadPool(2);
// Executer without context
//Execution 2
executor.submit(() -> {
try {
System.out.println(new InitialContext().lookup("java:comp/env/replySuccess"));
} catch (NamingException namingException) {
System.err.println("NamingException on ExecuterService as no context available.");
}
});
//Execution 3
executor.submit(contextService.contextualRunnable(() -> {
try {
System.out.println(new InitialContext().lookup("java:comp/env/replySuccess"));
} catch (NamingException namingException) {
namingException.printStackTrace();
}
}));
return "Application context propogated with java.util.concurrent.ExecutorService using jakarta.enterprise.concurrent.ContextService.";
}
..
In this example:
ManagedExecuterService
inExecution 1
is running with the Application Context and will be able to access JNDI lookup.ExecuterService
inExecution 2
will not have Application Context and will not be able to access JNDI lookup.- To get the context,
ExecuterService
inExecution 3
is paired with theContextService
.
The output of the above program in the console:
INFO] Hello from java:comp/env! The document was inserted successfully!
[INFO] [err] NamingException on ExecuterService as no context available.
[INFO] Hello from java:comp/env! The document was inserted successfully!
Managed Threads
With Jakarta Concurrency, developers can leverage managed threads to execute tasks in a controlled and managed environment. This helps mitigate common pitfalls associated with manual thread management, such as resource leaks and inefficient resource utilization.
Let’s dive into an example demonstrating the usage of managed threads:
..
@Resource
private ManagedThreadFactory managedThreadFactory;
@GET
@Path("managedThreads")
@Produces(MediaType.TEXT_PLAIN)
public String getManagedThreads() throws InterruptedException, ExecutionException {
// Create and start a managed thread
Thread thread = managedThreadFactory.newThread(() -> {
System.out.println("Managed thread is executing");
});
thread.start();
return "Managed thread created";
}
..
Embracing Best Practices
While Jakarta Concurrency offers powerful tools for concurrent programming, it’s essential to follow best practices to ensure robust and reliable applications:
- Use managed executors: Whenever possible, leverage managed executors for task execution to benefit from automatic thread lifecycle management and resource optimization.
- Contextual awareness: Ensure proper propagation of contextual information across asynchronous boundaries to maintain consistency and integrity in your applications.
- Avoid shared mutable state: Minimize the use of shared mutable state between concurrent tasks to reduce the risk of race conditions and synchronization issues.
- Error handling: Implement robust error handling and recovery mechanisms to gracefully handle exceptions and failures in concurrent execution paths.
Conclusion
Concurrency is a powerful tool in the arsenal of modern software development, but it comes with its own set of challenges. Jakarta Concurrency provides developers with a comprehensive set of tools and utilities to tackle these challenges effectively. By leveraging managed executors, context propagation, and asynchronous event handling, developers can design high-performance, scalable applications that harness the full potential of concurrency. However, it’s crucial to follow best practices and guidelines to ensure the reliability and stability of concurrent systems. With Jakarta Concurrency, developers can navigate the waves of concurrency with confidence, unlocking new possibilities in Java application development.
“Hello, world? Hold on, I’ll put you on hold, spawn a few more threads, and get back to you.”
Want to try it out? Go to the jakarta-concurrency-demo using Open Liberty.
Happy coding :)
Published at DZone with permission of Gautham Krishnan. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments