Class Monitor
This class is intended as a replacement for ReentrantLock
. Code using Monitor
is less error-prone and more readable than code using ReentrantLock
, without significant
performance loss. Monitor
even has the potential for performance gain by optimizing the
evaluation and signaling of conditions. Signaling is entirely implicit. By
eliminating explicit signaling, this class can guarantee that only one thread is awakened when a
condition becomes true (no "signaling storms" due to use of Condition.signalAll
) and that no signals are lost
(no "hangs" due to incorrect use of Condition.signal
).
A thread is said to occupy a monitor if it has entered the monitor but not yet left. Only one thread may occupy a given monitor at any moment. A monitor is also reentrant, so a thread may enter a monitor any number of times, and then must leave the same number of times. The enter and leave operations have the same synchronization semantics as the built-in Java language synchronization primitives.
A call to any of the enter methods with void return type should always be followed immediately by a try/finally block to ensure that the current thread leaves the monitor cleanly:
monitor.enter();
try {
// do things while occupying the monitor
} finally {
monitor.leave();
}
A call to any of the enter methods with boolean return type should always appear as the condition of an if statement containing a try/finally block to ensure that the current thread leaves the monitor cleanly:
if (monitor.tryEnter()) {
try {
// do things while occupying the monitor
} finally {
monitor.leave();
}
} else {
// do other things since the monitor was not available
}
Comparison with synchronized
and ReentrantLock
The following examples show a simple threadsafe holder expressed using synchronized
,
ReentrantLock
, and Monitor
.
synchronized
This version is the fewest lines of code, largely because the synchronization mechanism used
is built into the language and runtime. But the programmer has to remember to avoid a couple of
common bugs: The wait()
must be inside a while
instead of an if
, and
notifyAll()
must be used instead of notify()
because there are two different
logical conditions being awaited.
public class SafeBox<V> {
private V value;
public synchronized V get() throws InterruptedException {
while (value == null) {
wait();
}
V result = value;
value = null;
notifyAll();
return result;
}
public synchronized void set(V newValue) throws InterruptedException {
while (value != null) {
wait();
}
value = newValue;
notifyAll();
}
}
ReentrantLock
This version is much more verbose than the synchronized
version, and still suffers
from the need for the programmer to remember to use while
instead of if
. However,
one advantage is that we can introduce two separate Condition
objects, which allows us to
use signal()
instead of signalAll()
, which may be a performance benefit.
public class SafeBox<V> {
private V value;
private final ReentrantLock lock = new ReentrantLock();
private final Condition valuePresent = lock.newCondition();
private final Condition valueAbsent = lock.newCondition();
public V get() throws InterruptedException {
lock.lock();
try {
while (value == null) {
valuePresent.await();
}
V result = value;
value = null;
valueAbsent.signal();
return result;
} finally {
lock.unlock();
}
}
public void set(V newValue) throws InterruptedException {
lock.lock();
try {
while (value != null) {
valueAbsent.await();
}
value = newValue;
valuePresent.signal();
} finally {
lock.unlock();
}
}
}
Monitor
This version adds some verbosity around the Guard
objects, but removes that same
verbosity, and more, from the get
and set
methods. Monitor
implements the
same efficient signaling as we had to hand-code in the ReentrantLock
version above.
Finally, the programmer no longer has to hand-code the wait loop, and therefore doesn't have to
remember to use while
instead of if
.
public class SafeBox<V> {
private V value;
private final Monitor monitor = new Monitor();
private final Monitor.Guard valuePresent = monitor.newGuard(() -> value != null);
private final Monitor.Guard valueAbsent = monitor.newGuard(() -> value == null);
public V get() throws InterruptedException {
monitor.enterWhen(valuePresent);
try {
V result = value;
value = null;
return result;
} finally {
monitor.leave();
}
}
public void set(V newValue) throws InterruptedException {
monitor.enterWhen(valueAbsent);
try {
value = newValue;
} finally {
monitor.leave();
}
}
}
- Since:
- 10.0
- Author:
- Justin T. Sampson, Martin Buchholz
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic class
A boolean condition for which a thread may wait. -
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionvoid
enter()
Enters this monitor.boolean
Enters this monitor.boolean
Enters this monitor.boolean
enterIf
(Monitor.Guard guard) Enters this monitor if the guard is satisfied.boolean
enterIf
(Monitor.Guard guard, long time, TimeUnit unit) Enters this monitor if the guard is satisfied.boolean
enterIf
(Monitor.Guard guard, Duration time) Enters this monitor if the guard is satisfied.boolean
Enters this monitor if the guard is satisfied.boolean
enterIfInterruptibly
(Monitor.Guard guard, long time, TimeUnit unit) Enters this monitor if the guard is satisfied.boolean
enterIfInterruptibly
(Monitor.Guard guard, Duration time) Enters this monitor if the guard is satisfied.void
Enters this monitor.boolean
enterInterruptibly
(long time, TimeUnit unit) Enters this monitor.boolean
enterInterruptibly
(Duration time) Enters this monitor.void
enterWhen
(Monitor.Guard guard) Enters this monitor when the guard is satisfied.boolean
enterWhen
(Monitor.Guard guard, long time, TimeUnit unit) Enters this monitor when the guard is satisfied.boolean
enterWhen
(Monitor.Guard guard, Duration time) Enters this monitor when the guard is satisfied.void
Enters this monitor when the guard is satisfied.boolean
enterWhenUninterruptibly
(Monitor.Guard guard, long time, TimeUnit unit) Enters this monitor when the guard is satisfied.boolean
enterWhenUninterruptibly
(Monitor.Guard guard, Duration time) Enters this monitor when the guard is satisfied.int
Returns the number of times the current thread has entered this monitor in excess of the number of times it has left.int
Returns an estimate of the number of threads waiting to enter this monitor.int
getWaitQueueLength
(Monitor.Guard guard) Returns an estimate of the number of threads waiting for the given guard to become satisfied.boolean
hasQueuedThread
(Thread thread) Queries whether the given thread is waiting to enter this monitor.boolean
Returns whether any threads are waiting to enter this monitor.boolean
hasWaiters
(Monitor.Guard guard) Queries whether any threads are waiting for the given guard to become satisfied.boolean
isFair()
Returns whether this monitor is using a fair ordering policy.boolean
Returns whether this monitor is occupied by any thread.boolean
Returns whether the current thread is occupying this monitor (has entered more times than it has left).void
leave()
Leaves this monitor.newGuard
(BooleanSupplier isSatisfied) Creates a new guard for this monitor.boolean
tryEnter()
Enters this monitor if it is possible to do so immediately.boolean
tryEnterIf
(Monitor.Guard guard) Enters this monitor if it is possible to do so immediately and the guard is satisfied.void
waitFor
(Monitor.Guard guard) Waits for the guard to be satisfied.boolean
waitFor
(Monitor.Guard guard, long time, TimeUnit unit) Waits for the guard to be satisfied.boolean
waitFor
(Monitor.Guard guard, Duration time) Waits for the guard to be satisfied.void
Waits for the guard to be satisfied.boolean
waitForUninterruptibly
(Monitor.Guard guard, long time, TimeUnit unit) Waits for the guard to be satisfied.boolean
waitForUninterruptibly
(Monitor.Guard guard, Duration time) Waits for the guard to be satisfied.
-
Constructor Details
-
Monitor
public Monitor()Creates a monitor with a non-fair (but fast) ordering policy. Equivalent toMonitor(false)
. -
Monitor
Creates a monitor with the given ordering policy.- Parameters:
fair
- whether this monitor should use a fair ordering policy rather than a non-fair (but fast) one
-
-
Method Details
-
newGuard
Creates a new guard for this monitor.- Parameters:
isSatisfied
- the new guard's boolean condition (seeisSatisfied()
)- Since:
- 21.0 (but only since 33.4.0 in the Android flavor)
-
enter
Enters this monitor. Blocks indefinitely. -
enter
-
enter
-
enterInterruptibly
Enters this monitor. Blocks indefinitely, but may be interrupted.- Throws:
InterruptedException
- if interrupted while waiting
-
enterInterruptibly
Enters this monitor. Blocks at most the given time, and may be interrupted.- Returns:
- whether the monitor was entered
- Throws:
InterruptedException
- if interrupted while waiting- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
enterInterruptibly
Enters this monitor. Blocks at most the given time, and may be interrupted.- Returns:
- whether the monitor was entered
- Throws:
InterruptedException
- if interrupted while waiting
-
tryEnter
Enters this monitor if it is possible to do so immediately. Does not block.Note: This method disregards the fairness setting of this monitor.
- Returns:
- whether the monitor was entered
-
enterWhen
Enters this monitor when the guard is satisfied. Blocks indefinitely, but may be interrupted.- Throws:
InterruptedException
- if interrupted while waiting
-
enterWhen
Enters this monitor when the guard is satisfied. Blocks at most the given time, including both the time to acquire the lock and the time to wait for the guard to be satisfied, and may be interrupted.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Throws:
InterruptedException
- if interrupted while waiting- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
enterWhen
Enters this monitor when the guard is satisfied. Blocks at most the given time, including both the time to acquire the lock and the time to wait for the guard to be satisfied, and may be interrupted.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Throws:
InterruptedException
- if interrupted while waiting
-
enterWhenUninterruptibly
Enters this monitor when the guard is satisfied. Blocks indefinitely. -
enterWhenUninterruptibly
Enters this monitor when the guard is satisfied. Blocks at most the given time, including both the time to acquire the lock and the time to wait for the guard to be satisfied.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
enterWhenUninterruptibly
Enters this monitor when the guard is satisfied. Blocks at most the given time, including both the time to acquire the lock and the time to wait for the guard to be satisfied.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
-
enterIf
Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but does not wait for the guard to be satisfied.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
-
enterIf
Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the lock, but does not wait for the guard to be satisfied.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
enterIf
Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the lock, but does not wait for the guard to be satisfied.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
-
enterIfInterruptibly
Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but does not wait for the guard to be satisfied, and may be interrupted.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Throws:
InterruptedException
- if interrupted while waiting
-
enterIfInterruptibly
Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the lock, but does not wait for the guard to be satisfied, and may be interrupted.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Throws:
InterruptedException
- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
enterIfInterruptibly
public boolean enterIfInterruptibly(Monitor.Guard guard, long time, TimeUnit unit) throws InterruptedException Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the lock, but does not wait for the guard to be satisfied, and may be interrupted.- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
- Throws:
InterruptedException
-
tryEnterIf
Enters this monitor if it is possible to do so immediately and the guard is satisfied. Does not block acquiring the lock and does not wait for the guard to be satisfied.Note: This method disregards the fairness setting of this monitor.
- Returns:
- whether the monitor was entered, which guarantees that the guard is now satisfied
-
waitFor
Waits for the guard to be satisfied. Waits indefinitely, but may be interrupted. May be called only by a thread currently occupying this monitor.- Throws:
InterruptedException
- if interrupted while waiting
-
waitFor
Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May be called only by a thread currently occupying this monitor.- Returns:
- whether the guard is now satisfied
- Throws:
InterruptedException
- if interrupted while waiting- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
waitFor
Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May be called only by a thread currently occupying this monitor.- Returns:
- whether the guard is now satisfied
- Throws:
InterruptedException
- if interrupted while waiting
-
waitForUninterruptibly
Waits for the guard to be satisfied. Waits indefinitely. May be called only by a thread currently occupying this monitor. -
waitForUninterruptibly
Waits for the guard to be satisfied. Waits at most the given time. May be called only by a thread currently occupying this monitor.- Returns:
- whether the guard is now satisfied
- Since:
- 28.0 (but only since 33.4.0 in the Android flavor)
-
waitForUninterruptibly
Waits for the guard to be satisfied. Waits at most the given time. May be called only by a thread currently occupying this monitor.- Returns:
- whether the guard is now satisfied
-
leave
Leaves this monitor. May be called only by a thread currently occupying this monitor. -
isFair
Returns whether this monitor is using a fair ordering policy. -
isOccupied
Returns whether this monitor is occupied by any thread. This method is designed for use in monitoring of the system state, not for synchronization control. -
isOccupiedByCurrentThread
Returns whether the current thread is occupying this monitor (has entered more times than it has left). -
getOccupiedDepth
Returns the number of times the current thread has entered this monitor in excess of the number of times it has left. Returns 0 if the current thread is not occupying this monitor. -
getQueueLength
Returns an estimate of the number of threads waiting to enter this monitor. The value is only an estimate because the number of threads may change dynamically while this method traverses internal data structures. This method is designed for use in monitoring of the system state, not for synchronization control. -
hasQueuedThreads
Returns whether any threads are waiting to enter this monitor. Note that because cancellations may occur at any time, atrue
return does not guarantee that any other thread will ever enter this monitor. This method is designed primarily for use in monitoring of the system state. -
hasQueuedThread
Queries whether the given thread is waiting to enter this monitor. Note that because cancellations may occur at any time, atrue
return does not guarantee that this thread will ever enter this monitor. This method is designed primarily for use in monitoring of the system state. -
hasWaiters
Queries whether any threads are waiting for the given guard to become satisfied. Note that because timeouts and interrupts may occur at any time, atrue
return does not guarantee that the guard becoming satisfied in the future will awaken any threads. This method is designed primarily for use in monitoring of the system state. -
getWaitQueueLength
Returns an estimate of the number of threads waiting for the given guard to become satisfied. Note that because timeouts and interrupts may occur at any time, the estimate serves only as an upper bound on the actual number of waiters. This method is designed for use in monitoring of the system state, not for synchronization control.
-