Proper synchronization and thread management can be tricky under the best of circumstances, but it’s particularly difficult in Jakarta EE application, and is even forbidden under some circumstances by the Jakarta EE standard.
This rule raises an issue for each Runnable, and use of the synchronized keyword in Jakarta EE applications.
Instead of creating unmanaged threads, use the Concurrency Utilities for Jakarta EE. Injecting a ManagedExecutorService lets the
container manage the thread pool and preserves the container context (CDI, security, transactions). If you are using EJBs, annotate a method with
@Asynchronous to run it in a container-managed background thread, with no need to manage threads or submit tasks explicitly.
For shared mutable state that would otherwise require synchronized, use container-managed concurrency instead. In EJBs, the container
serializes access to @Singleton beans by default (via @Lock(WRITE)), eliminating the need for explicit synchronization.
Outside EJBs, use the concurrency utilities from java.util.concurrent such as AtomicInteger or
ConcurrentHashMap, which are safe to use in Jakarta EE.
@RequestScoped
public class DataProcessor {
public void processData() {
Runnable task = () -> { // Noncompliant
// ...
};
new Thread(task).start();
}
}
Using ManagedExecutorService
@RequestScoped
public class DataProcessor {
@Resource
private ManagedExecutorService executor;
public void processData() {
executor.submit(() -> { // Compliant
// ...
});
}
}
@Stateless
public class OrderService {
public void dispatchOrder(Long orderId) {
Runnable r = new Runnable() { // Noncompliant
public void run() {
// ...
}
};
new Thread(r).start();
}
}
Using @Asynchronous
@Stateless
public class OrderService {
@Asynchronous // Compliant
public void dispatchOrder(Long orderId) {
// ...
}
}
@Singleton
public class CounterService {
private int count = 0;
public synchronized void increment() { // Noncompliant
count++;
}
public synchronized int getCount() { // Noncompliant
return count;
}
}
Using @Singleton with @Lock(…)
@Singleton
public class CounterService {
private int count = 0;
@Lock(LockType.WRITE) // Compliant - container manages concurrency
public void increment() {
count++;
}
@Lock(LockType.READ) // Compliant
public int getCount() {
return count;
}
}