Why is this an issue?

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.

How to fix it

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.

Code examples

Noncompliant code example

@RequestScoped
public class DataProcessor {

  public void processData() {
    Runnable task = () -> { // Noncompliant
      // ...
    };
    new Thread(task).start();
  }
}

Compliant solution

Using ManagedExecutorService

@RequestScoped
public class DataProcessor {

  @Resource
  private ManagedExecutorService executor;

  public void processData() {
    executor.submit(() -> { // Compliant
      // ...
    });
  }
}

Noncompliant code example

@Stateless
public class OrderService {

  public void dispatchOrder(Long orderId) {
    Runnable r = new Runnable() { // Noncompliant
      public void run() {
        // ...
      }
    };
    new Thread(r).start();
  }
}

Compliant solution

Using @Asynchronous

@Stateless
public class OrderService {

  @Asynchronous // Compliant
  public void dispatchOrder(Long orderId) {
    // ...
  }
}

Noncompliant code example

@Singleton
public class CounterService {

  private int count = 0;

  public synchronized void increment() { // Noncompliant
    count++;
  }

  public synchronized int getCount() { // Noncompliant
    return count;
  }
}

Compliant solution

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;
  }
}

Resources