CycleDetectingLockFactory creates ReentrantLock instances and ReentrantReadWriteLock instances that detect potential deadlock by checking for cycles in lock
acquisition order.
Potential deadlocks detected when calling the lock(), lockInterruptibly(), or
tryLock() methods will result in the execution of the Policy specified when
creating the factory. The currently available policies are:
The locks created by a factory instance will detect lock acquisition cycles with locks created
by other CycleDetectingLockFactory instances (except those with Policy.DISABLED).
A lock's behavior when a cycle is detected, however, is defined by the Policy of the
factory that created it. This allows detection of cycles across components while delegating
control over lock behavior to individual components.
Applications are encouraged to use a CycleDetectingLockFactory to create any locks for
which external/unmanaged code is executed while the lock is held. (See caveats under
Performance).
Cycle Detection
Deadlocks can arise when locks are acquired in an order that forms a cycle. In a simple example involving two locks and two threads, deadlock occurs when one thread acquires Lock A, and then Lock B, while another thread acquires Lock B, and then Lock A:
Thread1: acquire(LockA) --X acquire(LockB) Thread2: acquire(LockB) --X acquire(LockA)
Neither thread will progress because each is waiting for the other. In more complex applications, cycles can arise from interactions among more than 2 locks:
Thread1: acquire(LockA) --X acquire(LockB) Thread2: acquire(LockB) --X acquire(LockC) ... ThreadN: acquire(LockN) --X acquire(LockA)
The implementation detects cycles by constructing a directed graph in which each lock represents a node and each edge represents an acquisition ordering between two locks.
Note that detection of potential deadlock does not necessarily indicate that deadlock will happen, as it is possible that higher level application logic prevents the cyclic lock acquisition from occurring. One example of a false positive is:
LockA -> LockB -> LockC LockA -> LockC -> LockB
ReadWriteLocks
While ReadWriteLock instances have different properties and can form cycles without
potential deadlock, this class treats ReadWriteLock instances as equivalent to
traditional exclusive locks. Although this increases the false positives that the locks detect
(i.e. cycles that will not actually result in deadlock), it simplifies the algorithm and
implementation considerably. The assumption is that a user of this factory wishes to eliminate
any cyclic acquisition ordering.
Explicit Lock Acquisition Ordering
The CycleDetectingLockFactory. class can be used to enforce an
application-specific ordering in addition to performing general cycle detection.
Garbage Collection
In order to allow proper garbage collection of unused locks, the edges of the lock graph are weak references.
Performance
The extra bookkeeping done by cycle detecting locks comes at some cost to performance. Benchmarks (as of December 2011) show that:
lock() and unlock(), a cycle detecting lock takes 38ns as
opposed to the 24ns taken by a plain lock.
As such, the CycleDetectingLockFactory may not be suitable for performance-critical applications which involve tightly-looped or deeply-nested locking algorithms.
| Modifier and Type | Class and Description |
|---|---|
| private static interface | CycleDetectingLockFactory.
Internal Lock implementations implement the |
| pack-priv class | |
| private class | |
| pack-priv class | |
| private class | |
| private static class | CycleDetectingLockFactory.
A Throwable used to record a stack trace that illustrates an example of a specific lock acquisition ordering. |
| private static class | CycleDetectingLockFactory.
A |
| public static enum | CycleDetectingLockFactory.
Pre-defined |
| public static interface | CycleDetectingLockFactory.
Encapsulates the action to be taken when a potential deadlock is encountered. |
| public static class | CycleDetectingLockFactory.
Represents a detected cycle in lock acquisition ordering. |
| public static class | CycleDetectingLockFactory.
The Enum type representing the explicit lock ordering. E extends EnumA |
| Modifier and Type | Field and Description |
|---|---|
| private static final ThreadLocal | acquiredLocks
Tracks the currently acquired locks for each Thread, kept up to date by calls to |
| private static final ConcurrentMap | |
| private static final LazyLogger | |
| pack-priv final CycleDetectingLockFactory. |
| Access | Constructor and Description |
|---|---|
| private |
| Modifier and Type | Method and Description |
|---|---|
| private void | aboutToAcquire(CycleDetectingLockFactory.
CycleDetectingLock implementations must call this method before attempting to acquire the lock. |
| pack-priv static <E extends Enum | createNodes(Class<E> clazz)
For a given Enum type, creates an immutable map from each of the Enum's values to a
corresponding LockGraphNode, with the |
| private static String | getLockName(Enum<?> rank)
For the given Enum value |
| private static <E extends Enum | |
| private static void | lockStateChanged(CycleDetectingLockFactory.
CycleDetectingLock implementations must call this method in a |
| public static CycleDetectingLockFactory | newInstance(CycleDetectingLockFactory.
Creates a new factory with the specified policy. |
| public static <E extends Enum | newInstanceWithExplicitOrdering(Class<E> enumClass, CycleDetectingLockFactory.
Creates a |
| public ReentrantLock | |
| public ReentrantLock | newReentrantLock(String lockName, boolean fair)
Creates a |
| public ReentrantReadWriteLock | |
| public ReentrantReadWriteLock | newReentrantReadWriteLock(String lockName, boolean fair)
Creates a |