Java EE EJB concurrency (ConcurrencyManagement, Lock and LockType)
Stateless EJB's
Even though Stateless EJB's may never be concurrently accessed by more than a single client call, this article would be otherwise incomplete if it did not mention this kind of EJB's.
Stateless EJB's are pooled by the container. Every time a client makes a call to a Stateless EJB, the container will fetch an available instance from the pool to handle the client request. When that instance is handling the client request, that same instance will not handle any other call that is made from any given client. When that call ends, the EJB instance is returned to the pool and becomes once again available to handle client requests.
If there is no available EJB instance in the pool to handle a client request (ex: the pooled EJB instances are all busy handling other requests), the container will create a new instance, put it in the pool, and let it handle the incoming client request.
This pool is usually configured by application server, and if it becomes exhausted when a client request arrives (ex: the pool has reached its maximum size) an exception will be thrown and propagated to the remote client.
Even if there are multiple consecutive calls from the same client to the same Stateless EJB, it should never be assumed that those consecutive calls will be handled by the same EJB instance.
@Stateless public class StatelessEJB { public void doSomething(){ } }
@EJB private StatelessEJB statelessEJB; public void clientMethod() { // These consecutive calls are NOT guaranteed to // be handled by the same Stateless EJB instance statelessEJB.doSomething(); statelessEJB.doSomething(); }
Container managed concurrency
As opposed to Stateless EJB's, Singleton and Stateful EJB's may be configured to handle concurrent requests from any given client.
Container managed concurrency configuration applies both to Singleton and Stateful EJB's - keep in mind that Stateful EJB's concurrency strategy will always consist in WRITE (exclusive) locking semantics. It allows, in conjunction with Lock and LockType primitives, to configure how concurrent access should be allowed by the container while handling EJB calls, with method level granularity:
@Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) public class SingletonEJB { int counter; @Lock(LockType.READ) public int readCounter() { return counter; } @Lock(LockType.WRITE) public void incrementCounter() { counter++; } }
By default, every Stateful and Singleton EJB has container managed concurrency so, in this example, the annotation @ConcurrencyManagement with its value defined as CONTAINER is redundant.
The Lock annotation may have a couple of distinct values: READ and WRITE. Methods annotated with READ lock type may be accessed concurrently by any arbitrary number of clients, given the fact that there is no method configured with WRITE lock type being accessed at that moment.
WRITE lock type methods are exclusive, ie. if a WRITE locked method is being accessed at a given moment, no other method - READ or WRITE - may be accessed until the method execution completes. Since WRITE methods are exclusive, if a WRITE method is ready to be executed but there is(are) other method(s) executing at that time, it must wait for the them to complete in order to acquire the exclusive lock and proceed.
It's important to mention that the Lock annotation may also be defined at class level, meaning that the specified access type will then be applied to all EJB methods. We may override the behavior for specific methods by applying the annotation to a given method:
@Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) @Lock(LockType.READ) public class SingletonEJB { int counter; // This method will inherit the Lock // configuration defined at class level public int readCounter() { return counter; } // This method overrides the class level // Lock semantics by changing it to WRITE @Lock(LockType.WRITE) public void incrementCounter() { counter++; } }
We end this section mentioning the access timeout configuration. One may additionally configure the amount of time that a client request will wait for concurrent method access, before giving up. Once the timeout is reached, the container will throw an exception which will be propagated to the calling client. The timeout is configured by the @AccessTimeout annotation, which may be defined at both class and method level (the method level configuration overrides the class level configuration):
@Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) @AccessTimeout(value = 5000) public class SingletonEJB { int counter; @Lock(LockType.READ) @AccessTimeout(value = 2, unit = TimeUnit.SECONDS) public int readCounter() { return counter; } @Lock(LockType.WRITE) public void incrementCounter() { counter++; } }
The Access Timeout annotation may be configured with value and unit properties: the value property specifies the amount of time before the timeout is reached, while the unit - as the name states - specifies the unit in which the time property value is defined. The default time unit is MILLISECONDS.
In this example, the incrementCounter() method will have a timeout of 5 seconds (inherited from the class level configuration), while the method readCounter() will have a timeout of 2 seconds (overriding the class level configuration).
Bean managed concurrency
As the ConcurrencyManagement documentation states, bean managed concurrency is only applied to Singleton EJB's.
Bean managed concurrency is defined like the following:
@Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) public class SingletonEJB { public void someMethod() { } }
Bean managed concurrency means that no Java EE primitives, like Lock and LockType, may be used to manage concurrent access to a given Singleton EJB. The application is instead responsible to manage concurrent access by the means of the traditional concurrent access primitives, like synchronized, volatile, wait, notify or any other data structure or library that provides concurrent access management.