Reentrant means a thread that already holds a monitor can acquire it again without blocking on itself; the JVM tracks ownership per-thread with a recursion (hold) count, incrementing on each re-entry and releasing the lock only when the count returns to zero.
How it works
The monitor records its owning thread and a counter. monitorenter by the owner just bumps the counter; monitorexit decrements it. The lock is actually released for other threads only when the counter hits 0.
Thread-A: enter outer() -> owner=A, count=1 Thread-A: enter inner() -> owner=A, count=2 (no block!) Thread-A: exit inner() -> count=1 (still held) Thread-A: exit outer() -> count=0 (lock now free)
Why it matters: self-deadlock would be the alternative
The most common case is one synchronized method calling another on the same object:
class Account {
private int balance;
public synchronized void deposit(int amt) { // acquires 'this'
balance += amt;
}
public synchronized void transferIn(int amt) { // acquires 'this'...
deposit(amt); // ...re-acquires 'this'
} // OK because reentrant
}
Without reentrancy, transferIn would already hold this when it called deposit, and deposit's attempt to lock this would block forever — a thread deadlocking against itself. Reentrancy makes intra-object method calls and inheritance (a subclass synchronized override calling super which is also synchronized) just work.
Scope of the property
- Intrinsic locks (
synchronized) are reentrant — this is a language guarantee. ReentrantLockis reentrant by name and design (same hold-count mechanism, exposed viagetHoldCount()).- Note:
ReentrantReadWriteLockis reentrant, but you cannot upgrade a held read lock to a write lock (that deadlocks); downgrading write to read is allowed.