...
Singletons support two modes of concurrent access, Container-Managed Concurrency (the default) and Bean-Managed Concurrency.
Bean-Managed Concurrency
With Bean-Managed Concurrency, annotated as @ConcurrencyManagement(BEAN), the container sends all invocations into the bean and lets the Singleton bean instance decide how and when to synchronize access, if at all. Here the 'synchronization' keyword is allowed as well as the full javax.util.concurrent set of libraries.
Container-Managed Concurrency
With Container-Managed Concurrency, annotated as @ConcurrencyManagement(CONTAINER), the container will enforce concurrency for you via locking method access to the bean. Two modes, called locks exist and can be assigned to both the bean class and methods of the bean class.
Lock type
The first and the default is a "write" lock, annotated as @Lock(WRITE). Essentially, with a write lock , the caller hold holds an exclusive lock on the bean for the duration of the method call and all other threads for that or any other method must wait.
The second option is a "read" lock, annotated as @Lock(READ). The read lock allows full concurrent access to the methods (assuming no write locks are held). The default mode of "write" essentially makes your bean a single-threaded bean, which is very slow. The more conservative @Lock(WRITE) as was chosen as the default as this is how all the other bean types work (on only a single thread may access a bean instance at any given time). Those that are aware of how to handle concurrent access can easily put @Lock(READ) on their bean class, thus changing the default, and then @Lock(WRITE) on specific methods if needed.
...
- @Lock(READ) == theSingletonReadWriteLock.readLock().lock()
- @Lock(WRITE) == theSingletonReadWriteLock.writeLock().lock()
The EJB container may not use a something other than ReadWriteLock but the semantics of a ReadWriteLock must be followed. Internally, we use an instance of java.util.concurrent.ReentrantReadWriteLock which supports correct memory synchronization, some reentrancy, lock downgrading, and more.
Acquiring the Lock
The @AccessTimetout annotation can configure how long a thread will wait to acquire the read or write lock. This annotation can be used on the bean class or individual methods. The annotation maps directly to the java.util.concurrent.locks.Lock interface.
Code Block | ||
---|---|---|
| ||
public interface Lock {
/**
* Blocks (potentially) forever
*
* @AccessTimout with a value of -1
*/
void lock();
/**
* Non-blocking
*
* @AccessTimout with a value of 0
*/
boolean tryLock();
/**
* Blocks (potentially) up to a limit
*
* @AccessTimout(30, TimeUnit.SECONDS)
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
}
|
In the event it is not possible to acquire the lock a javax.ejb.ConcurrentAccessException or javax.ejb.ConcurrentAccessTimeoutException will be thrown.
Default Timeout
The default value of @AccessTimeout annotation is vendor specific. In OpenEJB it defaults to the value of the AccessTimeout property which can be configured in many different scopes. Here is the order of preference:
- bean-level in openejb-jar.xml/<openejb-jar>/<ejb-deployment>/<properties>
- jar-level in openejb-jar.xml/<openejb-jar>/<properties>
- container-level in openejb.xml/<openejb>/<Container>
- boot-level via InitialContext(Properties) or EJBContainer.createEjbContainer(Map<Object,Object>)
- system-level in System.getProperties()
The value of the property can be phrased in plain english such as "1 hour and 23 minutes and 17 seconds" see Configuring Durations for details.
Startup and Startup Ordering
...