A Condition lets a thread release its associated lock while waiting for a protected predicate to become true, then reacquire that lock before continuing.
Java conditions: protected predicates and timed waiting
Java 8+. This is a complete program using JDK classes.
Wait for state, not a message
A receiving desk waits for an inventory count greater than zero. A signal says that a state change may have occurred; it does not hand the waiting thread a reserved item. Another consumer can take that item before the awakened thread reacquires the lock.
Check the predicate in a while loop. The loop handles both competing consumers and spurious wakeups. The code decrements its count only while holding the same lock used by the producer. Updating the count outside that lock would separate the signal from the state that the waiting code actually observes.
The method accepts a remaining nanosecond budget. awaitNanos reports what remains so repeated wakeups do not restart the full wait. Lock acquisition uses lockInterruptibly, making interruption an explicit way to leave both the admission and waiting phases.
Keep cleanup outside the success assumption
The finally block releases the lock after success, timeout or a waiting exception. A timed-out consumer returns false without inventing an item. Production shutdown needs another protected predicate, such as closed, and a signalAll so all waiting callers can recheck that state.
Working program
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReceivingDeskCondition {
final ReentrantLock lock = new ReentrantLock();
final Condition available = lock.newCondition();
int parcels;
void receive() {
lock.lock();
try { parcels++; available.signal(); }
finally { lock.unlock(); }
}
boolean take(long budgetNanos) throws InterruptedException {
lock.lockInterruptibly();
try {
while (parcels == 0) {
if (budgetNanos <= 0) return false;
budgetNanos = available.awaitNanos(budgetNanos);
}
parcels--; return true;
} finally { lock.unlock(); }
}
public static void main(String[] args) throws Exception {
ReceivingDeskCondition desk = new ReceivingDeskCondition();
System.out.println(desk.take(0));
desk.receive();
System.out.println(desk.take(0));
System.out.println(desk.take(0));
}
}Output
false
true
falseCosts and boundaries
The protected state transition is O(1), but a waiting call can spend its budget blocked. That budget applies to condition waiting, not the earlier lock acquisition or a complete method deadline. Scheduling and lock contention are not bounded by the few arithmetic instructions. This fixture verifies timeout and state transitions without relying on thread timing; the download checks also exercise a signalled waiter.
Common Mistakes
- An if check cannot protect against repeated wakeups.
- Use the same lock for the predicate, update and waiting operation.
- Signal does not transfer ownership of an item.
