@Beta @GwtIncompatible public final class Monitor extends java.lang.Object
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
}
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();
}
}
}
Modifier and Type | Class and Description |
---|---|
static class |
Monitor.Guard
A boolean condition for which a thread may wait.
|
Modifier and Type | Field and Description |
---|---|
private Monitor.Guard |
activeGuards
The guards associated with this monitor that currently have waiters (
waiterCount > 0 ). |
private boolean |
fair
Whether this monitor is fair.
|
private java.util.concurrent.locks.ReentrantLock |
lock
The lock underlying this monitor.
|
Constructor and Description |
---|
Monitor()
Creates a monitor with a non-fair (but fast) ordering policy.
|
Monitor(boolean fair)
Creates a monitor with the given ordering policy.
|
Modifier and Type | Method and Description |
---|---|
private void |
await(Monitor.Guard guard,
boolean signalBeforeWaiting) |
private boolean |
awaitNanos(Monitor.Guard guard,
long nanos,
boolean signalBeforeWaiting)
Caller should check before calling that guard is not satisfied.
|
private void |
awaitUninterruptibly(Monitor.Guard guard,
boolean signalBeforeWaiting) |
private void |
beginWaitingFor(Monitor.Guard guard)
Records that the current thread is about to wait on the specified guard.
|
private void |
endWaitingFor(Monitor.Guard guard)
Records that the current thread is no longer waiting on the specified guard.
|
void |
enter()
Enters this monitor.
|
boolean |
enter(java.time.Duration time)
Enters this monitor.
|
boolean |
enter(long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor.
|
boolean |
enterIf(Monitor.Guard guard)
Enters this monitor if the guard is satisfied.
|
boolean |
enterIf(Monitor.Guard guard,
java.time.Duration time)
Enters this monitor if the guard is satisfied.
|
boolean |
enterIf(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor if the guard is satisfied.
|
boolean |
enterIfInterruptibly(Monitor.Guard guard)
Enters this monitor if the guard is satisfied.
|
boolean |
enterIfInterruptibly(Monitor.Guard guard,
java.time.Duration time)
Enters this monitor if the guard is satisfied.
|
boolean |
enterIfInterruptibly(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor if the guard is satisfied.
|
void |
enterInterruptibly()
Enters this monitor.
|
boolean |
enterInterruptibly(java.time.Duration time)
Enters this monitor.
|
boolean |
enterInterruptibly(long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor.
|
void |
enterWhen(Monitor.Guard guard)
Enters this monitor when the guard is satisfied.
|
boolean |
enterWhen(Monitor.Guard guard,
java.time.Duration time)
Enters this monitor when the guard is satisfied.
|
boolean |
enterWhen(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor when the guard is satisfied.
|
void |
enterWhenUninterruptibly(Monitor.Guard guard)
Enters this monitor when the guard is satisfied.
|
boolean |
enterWhenUninterruptibly(Monitor.Guard guard,
java.time.Duration time)
Enters this monitor when the guard is satisfied.
|
boolean |
enterWhenUninterruptibly(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Enters this monitor when the guard is satisfied.
|
int |
getOccupiedDepth()
Returns the number of times the current thread has entered this monitor in excess of the number
of times it has left.
|
int |
getQueueLength()
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(java.lang.Thread thread)
Queries whether the given thread is waiting to enter this monitor.
|
boolean |
hasQueuedThreads()
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.
|
private static long |
initNanoTime(long timeoutNanos)
Returns System.nanoTime() unless the timeout has already elapsed.
|
boolean |
isFair()
Returns whether this monitor is using a fair ordering policy.
|
boolean |
isOccupied()
Returns whether this monitor is occupied by any thread.
|
boolean |
isOccupiedByCurrentThread()
Returns whether the current thread is occupying this monitor (has entered more times than it
has left).
|
private boolean |
isSatisfied(Monitor.Guard guard)
Exactly like guard.isSatisfied(), but in addition signals all waiting threads in the (hopefully
unlikely) event that isSatisfied() throws.
|
void |
leave()
Leaves this monitor.
|
Monitor.Guard |
newGuard(java.util.function.BooleanSupplier isSatisfied)
Creates a new guard for this monitor.
|
private static long |
remainingNanos(long startTime,
long timeoutNanos)
Returns the remaining nanos until the given timeout, or 0L if the timeout has already elapsed.
|
private void |
signalAllWaiters()
Signals all threads waiting on guards.
|
private void |
signalNextWaiter()
Signals some other thread waiting on a satisfied guard, if one exists.
|
private static long |
toSafeNanos(long time,
java.util.concurrent.TimeUnit unit)
Returns unit.toNanos(time), additionally ensuring the returned value is not at risk of
overflowing or underflowing, by bounding the value between 0 and (Long.MAX_VALUE / 4) * 3.
|
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,
java.time.Duration time)
Waits for the guard to be satisfied.
|
boolean |
waitFor(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Waits for the guard to be satisfied.
|
void |
waitForUninterruptibly(Monitor.Guard guard)
Waits for the guard to be satisfied.
|
boolean |
waitForUninterruptibly(Monitor.Guard guard,
java.time.Duration time)
Waits for the guard to be satisfied.
|
boolean |
waitForUninterruptibly(Monitor.Guard guard,
long time,
java.util.concurrent.TimeUnit unit)
Waits for the guard to be satisfied.
|
private final boolean fair
private final java.util.concurrent.locks.ReentrantLock lock
private Monitor.Guard activeGuards
waiterCount > 0
).
A linked list threaded through the Guard.next field.public Monitor()
Monitor(false)
.public Monitor(boolean fair)
fair
- whether this monitor should use a fair ordering policy rather than a non-fair (but
fast) onepublic Monitor.Guard newGuard(java.util.function.BooleanSupplier isSatisfied)
isSatisfied
- the new guard's boolean condition (see isSatisfied()
)public void enter()
public boolean enter(java.time.Duration time)
public boolean enter(long time, java.util.concurrent.TimeUnit unit)
public void enterInterruptibly() throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean enterInterruptibly(java.time.Duration time) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean enterInterruptibly(long time, java.util.concurrent.TimeUnit unit) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean tryEnter()
Note: This method disregards the fairness setting of this monitor.
public void enterWhen(Monitor.Guard guard) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean enterWhen(Monitor.Guard guard, java.time.Duration time) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean enterWhen(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic void enterWhenUninterruptibly(Monitor.Guard guard)
public boolean enterWhenUninterruptibly(Monitor.Guard guard, java.time.Duration time)
public boolean enterWhenUninterruptibly(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit)
public boolean enterIf(Monitor.Guard guard)
public boolean enterIf(Monitor.Guard guard, java.time.Duration time)
public boolean enterIf(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit)
public boolean enterIfInterruptibly(Monitor.Guard guard) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean enterIfInterruptibly(Monitor.Guard guard, java.time.Duration time) throws java.lang.InterruptedException
java.lang.InterruptedException
public boolean enterIfInterruptibly(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit) throws java.lang.InterruptedException
java.lang.InterruptedException
public boolean tryEnterIf(Monitor.Guard guard)
Note: This method disregards the fairness setting of this monitor.
public void waitFor(Monitor.Guard guard) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean waitFor(Monitor.Guard guard, java.time.Duration time) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic boolean waitFor(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit) throws java.lang.InterruptedException
java.lang.InterruptedException
- if interrupted while waitingpublic void waitForUninterruptibly(Monitor.Guard guard)
public boolean waitForUninterruptibly(Monitor.Guard guard, java.time.Duration time)
public boolean waitForUninterruptibly(Monitor.Guard guard, long time, java.util.concurrent.TimeUnit unit)
public void leave()
public boolean isFair()
public boolean isOccupied()
public boolean isOccupiedByCurrentThread()
public int getOccupiedDepth()
public int getQueueLength()
public boolean hasQueuedThreads()
true
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.public boolean hasQueuedThread(java.lang.Thread thread)
true
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.public boolean hasWaiters(Monitor.Guard guard)
true
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.public int getWaitQueueLength(Monitor.Guard guard)
private static long toSafeNanos(long time, java.util.concurrent.TimeUnit unit)
private static long initNanoTime(long timeoutNanos)
private static long remainingNanos(long startTime, long timeoutNanos)
private void signalNextWaiter()
We manage calls to this method carefully, to signal only when necessary, but never losing a signal, which is the classic problem of this kind of concurrency construct. We must signal if the current thread is about to relinquish the lock and may have changed the state protected by the monitor, thereby causing some guard to be satisfied.
In addition, any thread that has been signalled when its guard was satisfied acquires the responsibility of signalling the next thread when it again relinquishes the lock. Unlike a normal Condition, there is no guarantee that an interrupted thread has not been signalled, since the concurrency control must manage multiple Conditions. So this method must generally be called when waits are interrupted.
On the other hand, if a signalled thread wakes up to discover that its guard is still not satisfied, it does *not* need to call this method before returning to wait. This can only happen due to spurious wakeup (ignorable) or another thread acquiring the lock before the current thread can and returning the guard to the unsatisfied state. In the latter case the other thread (last thread modifying the state protected by the monitor) takes over the responsibility of signalling the next waiter.
This method must not be called from within a beginWaitingFor/endWaitingFor block, or else the current thread's guard might be mistakenly signalled, leading to a lost signal.
private boolean isSatisfied(Monitor.Guard guard)
private void signalAllWaiters()
private void beginWaitingFor(Monitor.Guard guard)
private void endWaitingFor(Monitor.Guard guard)
private void await(Monitor.Guard guard, boolean signalBeforeWaiting) throws java.lang.InterruptedException
java.lang.InterruptedException
private void awaitUninterruptibly(Monitor.Guard guard, boolean signalBeforeWaiting)
private boolean awaitNanos(Monitor.Guard guard, long nanos, boolean signalBeforeWaiting) throws java.lang.InterruptedException
java.lang.InterruptedException