Top Description Inners Fields Constructors Methods
com.google.common.util.concurrent

public Class CycleDetectingLockFactory

extends Object
Class Inheritance
Known Direct Subclasses
com.google.common.util.concurrent.CycleDetectingLockFactory.WithExplicitOrdering
Annotations
@J2ktIncompatible
@GwtIncompatible
@ElementTypesAreNonnullByDefault
Static Imports
com.google.common.base.Preconditions.checkNotNull, java.util.Objects.requireNonNull

The 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.WithExplicitOrdering 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:

As such, the CycleDetectingLockFactory may not be suitable for performance-critical applications which involve tightly-looped or deeply-nested locking algorithms.

Author
Darick Tong
Since
13.0

Nested and Inner Type Summary

Modifier and TypeClass and Description
private static interface
CycleDetectingLockFactory.CycleDetectingLock

Internal Lock implementations implement the CycleDetectingLock interface, allowing the detection logic to treat all locks in the same manner.

pack-priv class
private class
pack-priv class
private class
private static class
CycleDetectingLockFactory.ExampleStackTrace

A Throwable used to record a stack trace that illustrates an example of a specific lock acquisition ordering.

private static class
CycleDetectingLockFactory.LockGraphNode

A LockGraphNode associated with each lock instance keeps track of the directed edges in the lock acquisition graph.

public static enum
CycleDetectingLockFactory.Policies

Pre-defined Policy implementations.

public static interface
CycleDetectingLockFactory.Policy

Encapsulates the action to be taken when a potential deadlock is encountered.

public static class
CycleDetectingLockFactory.PotentialDeadlockException

Represents a detected cycle in lock acquisition ordering.

public static class
CycleDetectingLockFactory.WithExplicitOrdering<
The Enum type representing the explicit lock ordering.
E extends Enum<E>
>

A CycleDetectingLockFactory.WithExplicitOrdering provides the additional enforcement of an application-specified ordering of lock acquisitions.

Field Summary

Modifier and TypeField and Description
private static final ThreadLocal<ArrayList<CycleDetectingLockFactory.LockGraphNode>>
acquiredLocks

Tracks the currently acquired locks for each Thread, kept up to date by calls to aboutToAcquire(CycleDetectingLock) and lockStateChanged(CycleDetectingLock).

private static final ConcurrentMap<Class<? extends Enum<?>>, Map<? extends Enum<?>, CycleDetectingLockFactory.LockGraphNode>>
private static final LazyLogger
pack-priv final CycleDetectingLockFactory.Policy

Constructor Summary

AccessConstructor and Description
private

Method Summary

Modifier and TypeMethod and Description
private void
aboutToAcquire(CycleDetectingLockFactory.CycleDetectingLock lock)

CycleDetectingLock implementations must call this method before attempting to acquire the lock.

pack-priv static <E extends Enum<E>> Map<E, CycleDetectingLockFactory.LockGraphNode>
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 allowedPriorLocks and disallowedPriorLocks prepopulated with nodes according to the natural ordering of the associated Enum values.

private static String
getLockName(Enum<?> rank)

For the given Enum value rank, returns the value's "EnumClass.name", which is used in exception and warning output.

private static <E extends Enum<E>> Map<? extends E, CycleDetectingLockFactory.LockGraphNode>
private static void
lockStateChanged(CycleDetectingLockFactory.CycleDetectingLock lock)

CycleDetectingLock implementations must call this method in a finally clause after any attempt to change the lock state, including both lock and unlock attempts.

public static CycleDetectingLockFactory
newInstance(CycleDetectingLockFactory.Policy policy)

Creates a new factory with the specified policy.

public static <E extends Enum<E>> CycleDetectingLockFactory.WithExplicitOrdering<E>
newInstanceWithExplicitOrdering(Class<E> enumClass, CycleDetectingLockFactory.Policy policy)

Creates a CycleDetectingLockFactory.WithExplicitOrdering<E>.

public ReentrantLock
newReentrantLock(String lockName)

Equivalent to newReentrantLock(lockName, false).

public ReentrantLock
newReentrantLock(String lockName, boolean fair)

Creates a ReentrantLock with the given fairness policy.

public ReentrantReadWriteLock
newReentrantReadWriteLock(String lockName)

Equivalent to newReentrantReadWriteLock(lockName, false).

public ReentrantReadWriteLock
newReentrantReadWriteLock(String lockName, boolean fair)

Creates a ReentrantReadWriteLock with the given fairness policy.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait