Jakarta Concurrency

We are working on a fresh, updated Jakarta EE Tutorial. This section hasn’t yet been updated.

This chapter describes Jakarta Concurrency spec.

Concurrency Basics

Concurrency is the concept of executing two or more tasks at the same time (in parallel). Tasks may include methods (functions), parts of a program, or even other programs. With current computer architectures, support for multiple cores and multiple processors in a single CPU is very common.

The Java Platform has always offered support for concurrent programming, which was the basis for implementing many of the services offered by Jakarta EE containers. Since Java SE 5, additional high-level API support for concurrency was provided by the java.util.concurrent package.

Threads and Processes

The two main concurrency concepts are processes and threads.

Processes are primarily associated with applications running on the operating system (OS). A process has specific runtime resources to interact with the underlying OS and allocate other resources, such as its own memory, just as the JVM process does. A JVM is in fact a process.

The Java programming language and platform are primarily concerned with threads.

Threads share some features with processes, since both consume resources from the OS or the execution environment. But threads are easier to create and consume many fewer resources than a process.

Because threads are so lightweight, any modern CPU that has a couple of cores and a few gigabytes of RAM can handle thousands of threads in a single JVM process. The precise number of threads will depend on the combined output of the CPU, OS, and RAM available, as well as on correct configuration (tuning) of the JVM.

Although concurrent programming solves many problems and can improve performance for most applications, there are a number of situations where multiple execution lines (threads or processes) can cause major problems. These situations include the following:

  • Deadlocks

  • Thread starvation

  • Concurrent accessing of shared resources

  • Situations when the program generates incorrect data

Main Components of the Concurrency Utilities

Concurrent resources are managed objects that provide concurrency capabilities to Jakarta EE applications. In GlassFish Server, you configure concurrent resources and then make them available for use by application components such as servlets and enterprise beans. Concurrent resources are accessed through JNDI lookup or resource injection.

The primary components of the concurrency utilities are as follows.

  • ManagedExecutorService: A managed executor service is used by applications to execute submitted tasks asynchronously. Tasks are executed on threads that are started and managed by the container. The context of the container is propagated to the thread executing the task.

    For example, by using an ManagedExecutorService.submit() call, a task, such as the GenerateReportTask, could be submitted to execute at a later time and then, by using the Future object callback, retrieve the result when it becomes available.

  • ManagedScheduledExecutorService: A managed scheduled executor service is used by applications to execute submitted tasks asynchronously at specific times. Tasks are executed on threads that are started and managed by the container. The context of the container is propagated to the thread executing the task. The API provides the scheduling functionality that allows users to set a specific date/time for the Task execution programmatically in the application.

  • ContextService: A context service is used to create dynamic proxy objects that capture the context of a container and enable applications to run within that context at a later time or be submitted to a Managed Executor Service. The context of the container is propagated to the thread executing the task.

  • ManagedThreadFactory: A managed thread factory is used by applications to create managed threads. The threads are started and managed by the container. The context of the container is propagated to the thread executing the task. This object can also be used to provide custom factories for specific use cases (with custom Threads) and, for example, set specific/proprietary properties to these objects.

Concurrency and Transactions

The most basic operations for transactions are commit and rollback, but, in a distributed environment with concurrent processing, it can be difficult to guarantee that commit or rollback operations will be successfully processed, and the transaction can be spread among different threads, CPU cores, physical machines, and networks.

Ensuring that a rollback operation will successfully execute in such a scenario is crucial. Concurrency Utilities relies on Jakarta Transactions to implement and support transactions on its components through jakarta.transaction.UserTransaction, allowing application developers to explicitly manage transaction boundaries. More information is available in the Jakarta Transactions specification.

Optionally, context objects can begin, commit, or roll back transactions, but these objects cannot enlist in parent component transactions.

The following code snippet illustrates a Runnable task that obtains a UserTransaction and then starts and commits a transaction while interacting with other transactional components, such as an enterprise bean and a database:

public class MyTransactionalTask implements Runnable {

   UserTransaction ut = ... // obtained through JNDI or injection

   public void run() {

       // Start a transaction
       ut.begin();

       // Invoke a Service or an EJB
       myEJB.businessMethod();

       // Update a database entity using an XA JDBC driver
       myEJB.updateCustomer(customer);

       // Commit the transaction
       ut.commit();

   }
}

Concurrency and Security

Jakarta Concurrency defers most security decisions to the application server implementation. If, however, the container supports a security context, that context can be propagated to the thread of execution. The ContextService can support several runtime behaviors, and the security attribute, if enabled, will propagate the container security principal.

The jobs Concurrency Example

This section describes a very basic example that shows how to use some of the basic concurrency features in an enterprise application. Specifically, this example uses one of the main components of Jakarta Concurrency, a Managed Executor Service.

The example demonstrates a scenario where a RESTful web service, exposed as a public API, is used to submit generic jobs for execution. These jobs are processed in the background. Each job prints a "Starting" and a "Finished" message at the beginning and end of the execution. Also, to simulate background processing, each job takes 10 seconds to execute.

The RESTful service exposes two methods:

  • /token: Exposed as a GET method that registers and returns valid API tokens

  • /process: Exposed as a POST method that receives a jobID query parameter, which is the identifier for the job to be executed, and a custom HTTP header named X-REST-API-Key, which will be used internally to validate requests with tokens

The token is used to differentiate the Quality of Service (QoS) offered by the API. Users that provide a token in a service request can process multiple concurrent jobs. However, users that do not provide a token can process only one job at a time. Since every job takes 10 seconds to execute, users that provide no token will be able to execute only one call to the service every 10 seconds. For users that provide a token, processing will be much faster.

This differentiation is made possible by the use of two different Managed Executor Services, one for each type of request.

Running the jobs Example

After configuring GlassFish Server by adding two Managed Executor Services, you can use either NetBeans IDE or Maven to build, package, deploy, and run the jobs example.

To Configure GlassFish Server for the Basic Concurrency Example

To configure GlassFish Server, follow these steps.

  1. Open the Administration Console at http://localhost:4848.

  2. Expand the Resources node.

  3. Expand the Concurrent Resources node.

  4. Click Managed Executor Services.

  5. On the Managed Executor Services page, click New to open the New Managed Executor Services page.

  6. In the JNDI Name field, enter MES_High to create the high-priority Managed Executor Service. Use the following settings (keep the default values for other settings):

    • Thread Priority: 10

    • Core Size: 2

    • Maximum Pool Size: 5

    • Task Queue Capacity: 2

  7. Click OK.

  8. On the On the Managed Executor Services page, click New again.

  9. In the JNDI Name field, enter MES_Low to create the low-priority Managed Executor Service. Use the following settings (keep the default values for other settings):

    • Thread Priority: 1

    • Core Size: 1

    • Maximum Pool Size: 1

    • Task Queue Capacity: 0

  10. Click OK.

To Build, Package, and Deploy the jobs Example Using NetBeans IDE

  1. Make sure that GlassFish Server has been started (see Starting and Stopping GlassFish Server).

  2. From the File menu, choose Open Project.

  3. In the Open Project dialog box, navigate to:

    jakartaee-examples/tutorial/concurrency
  4. Select the jobs folder.

  5. Click Open Project.

  6. In the Projects tab, right-click the jobs project and select Build.

    This command builds and deploys the application.

To Build, Package, and Deploy the jobs Example Using Maven

  1. Make sure that GlassFish Server has been started (see Starting and Stopping GlassFish Server).

  2. In a terminal window, go to:

    jakartaee-examples/tutorial/concurrency/jobs
  3. Enter the following command to build and deploy the application:

    mvn install

To Run the jobs Example and Submit Jobs with Low Priority

To run the example as a user who submits jobs with low priority, follow these steps:

  1. In a web browser, enter the following URL:

    http://localhost:8080/jobs
  2. In the Jobs Client page, enter the value 1 in the Enter a JobID field, enter nothing in the Enter a Token field, then click Submit Job.

    The following message should be displayed at the bottom of the page:

    Job 1 successfully submitted

    The server log includes the following messages:

    INFO:   Invalid or missing token!
    INFO:   Task started LOW-1
    INFO:   Job 1 successfully submitted
    INFO:   Task finished LOW-1

    You submitted a job with low priority. This means that you cannot submit another job for 10 seconds. If you try to do so, the RESTful API will return a service unavailable (HTTP 503) response and the following message will be displayed at the bottom of the page:

    Job 2 was NOT submitted

    The server log will include the following messages:

    INFO:   Invalid or missing token!
    INFO:   Job 1 successfully submitted
    INFO:   Task started LOW-1
    INFO:   Invalid or missing token!
    INFO:   Job 2 was NOT submitted
    INFO:   Task finished LOW-1

To Run the jobs Example and Submit Jobs with High Priority

To run the example as a user who submits jobs with high priority, follow these steps:

  1. In a web browser, enter the following URL:

    http://localhost:8080/jobs
  2. In the Jobs Client page, enter a value of one to ten digits in the Enter a JobID field.

  3. Click the here link on the line "Get a token here" to get a token. The page that displays the token will open in a new tab.

  4. Copy the token and return to the Jobs Client page.

  5. Paste the token in the Enter a Token field, then click Submit Job.

    A message like the following should be displayed at the bottom of the page:

    Job 11 successfully submitted

    The server log includes the following messages:

    INFO:   Token accepted. Execution with high priority.
    INFO:   Task started HIGH-11
    INFO:   Job 11 successfully submitted
    INFO:   Task finished HIGH-11

    You submitted a job with high priority. This means that you can submit multiple jobs, each with a token, and not face the 10 second per job restriction that the low priority submitters face. If you submit 3 jobs with tokens in rapid succession, messages like the following will be displayed at the bottom of the page:

    Job 1 was submitted
    Job 2 was submitted
    Job 3 was submitted

    The server log will include the following messages:

    INFO:   Token accepted. Execution with high priority.
    INFO:   Task started HIGH-1
    INFO:   Job 1 successfully submitted
    INFO:   Token accepted. Execution with high priority.
    INFO:   Task started HIGH-2
    INFO:   Job 2 successfully submitted
    INFO:   Task finished HIGH-1
    INFO:   Token accepted. Execution with high priority.
    INFO:   Task started HIGH-3
    INFO:   Job 3 successfully submitted
    INFO:   Task finished HIGH-2
    INFO:   Task finished HIGH-3

The taskcreator Concurrency Example

The taskcreator example demonstrates how to use Jakarta Concurrency to run tasks immediately, periodically, or after a fixed delay. This example provides a Jakarta Faces interface that enables users to submit tasks to be executed and displays information messages for each task. The example uses the Managed Executor Service to run tasks immediately and the Managed Scheduled Executor Service to run tasks periodically or after a fixed delay. (See Main Components of the Concurrency Utilities for information about these services.)

The taskcreator example consists of the following components.

  • A Jakarta Faces page (index.xhtml) that contains three elements: a form to submit tasks, a task execution log, and a form to cancel periodic tasks. This page submits Ajax requests to create and cancel tasks. This page also receives WebSocket messages, using JavaScript code to update the task execution log.

  • A CDI managed bean (TaskCreatorBean) that processes the requests from the Jakarta Faces page. This bean invokes the methods in TaskEJB to submit new tasks and to cancel periodic tasks.

  • An enterprise bean (TaskEJB) that obtains executor service instances using resource injection and submits tasks for execution. This bean is also a Jakarta RESTful web service endpoint. The tasks send information messages to this endpoint.

  • A WebSocket endpoint (InfoEndpoint) that the enterprise bean uses to send information messages to the clients.

  • A task class (Task) that implements the Runnable interface. The run method in this class sends information messages to the web service endpoint in TaskEJB and sleeps for 1.5 seconds.

Figure 1, “Architecture of the taskcreator Example” shows the architecture of the taskcreator example.

The figure shows the architecture of the taskcreator example. The Jakarta Faces page invokes methods on a CDI-managed bean, which submits task initiation requests to an enterprise bean. The enterprise bean uses a WebSocket endpoint to indicate to clients that an updated task execution log is available.
Figure 1. Architecture of the taskcreator Example

The TaskEJB class obtains the default executor service objects from the application server as follows:

@Resource(name="java:comp/DefaultManagedExecutorService")
ManagedExecutorService mExecService;

@Resource(name="java:comp/DefaultManagedScheduledExecutorService")
ManagedScheduledExecutorService sExecService;

The submitTask method in TaskEJB uses these objects to submit tasks for execution as follows:

public void submitTask(Task task, String type) {
    /* Use the managed executor objects from the app server */
    switch (type) {
        case "IMMEDIATE":
            mExecService.submit(task);
            break;
        case "DELAYED":
            sExecService.schedule(task, 3, TimeUnit.SECONDS);
            break;
        case "PERIODIC":
            ScheduledFuture<?> fut;
            fut = sExecService.scheduleAtFixedRate(task, 0, 8,
                    TimeUnit.SECONDS);
            periodicTasks.put(task.getName(), fut);
            break;
    }
}

For periodic tasks, TaskEJB keeps a reference to the ScheduledFuture object, so that the user can cancel the task at any time.

Running the taskcreator Example

This section describes how to build, package, deploy, and run the taskcreator example using NetBeans IDE or Maven.

To Build, Package, and Deploy the taskcreator Example Using NetBeans IDE

  1. Make sure that GlassFish Server has been started (see Starting and Stopping GlassFish Server).

  2. From the File menu, choose Open Project.

  3. In the Open Project dialog box, navigate to:

    jakartaee-examples/tutorial/concurrency
  4. Select the taskcreator folder.

  5. Click Open Project.

  6. In the Projects tab, right-click the taskcreator project and select Build.

    This command builds and deploys the application.

To Build, Package, and Deploy the taskcreator Example Using Maven

  1. Make sure that GlassFish Server has been started (see Starting and Stopping GlassFish Server).

  2. In a terminal window, go to:

    jakartaee-examples/tutorial/concurrency/taskcreator
  3. Enter the following command to build and deploy the application:

    mvn install

To Run the taskcreator Example

  1. Open the following URL in a web browser:

    http://localhost:8080/taskcreator/

    The page contains a form to submit tasks, a task execution log, and a form to cancel periodic tasks.

  2. Select the Immediate task type, enter a task name, and click the Submit button. Messages like the following appear in the task execution log:

    12:40:47 - IMMEDIATE Task TaskA finished
    12:40:45 - IMMEDIATE Task TaskA started
  3. Select the Delayed (3 sec) task type, enter a task name, and click the Submit button. Messages like the following appear in the task execution log:

    12:43:26 - DELAYED Task TaskB finished
    12:43:25 - DELAYED Task TaskB started
    12:43:22 - DELAYED Task TaskB submitted
  4. Select the Periodic (8 sec) task type, enter a task name, and click the Submit button. Messages like the following appear in the task execution log:

    12:45:25 - PERIODIC Task TaskC finished run #2
    12:45:23 - PERIODIC Task TaskC started run #2
    12:45:17 - PERIODIC Task TaskC finished run #1
    12:45:15 - PERIODIC Task TaskC started run #1

    You can add more than one periodic task. To cancel a periodic task, select it from the form and click Cancel Task.

Further Information about Jakarta Concurrency

For more information about concurrency, see