Reentrant Locks
A reentrant lock can be acquired again by the thread that already holds it, incrementing a hold count — preventing self-deadlock when code paths legitimately re-enter guarded sections.
Definition
Reentrancy lets the owning thread acquire the same lock multiple times, tracked by a hold counter released by matching unlock calls. Synchronized blocks in Java and critical sections in most threading libraries are reentrant. It prevents self-deadlock from nested calls into guarded methods at the cost of hiding accidental recursive locking that indicates design smell.
Context and scope
Default in Java synchronized and ReentrantLock, Python RLock, and C++ recursive_mutex. Contrast with non-reentrant designs where re-acquisition deadlocks immediately, exposing the recursion at once.
Common misconceptions
- Reentrant locks prevent all deadlocks — they prevent only single-thread self-deadlock; multi-thread cycles remain fully possible
- Reentrancy is free performance — hold counting adds overhead to every acquire and release
- Recursive locking is a design smell that reentrancy merely silences — re-entrant paths through guarded code indicate the invariant should be split or the lock scope reduced
Anti-patterns
- Building deep call chains through guarded public methods and leaning on reentrancy instead of restructuring
- Acquiring in a loop and releasing once, corrupting the hold count
- Assuming condition variables and reentrancy semantics compose the same across languages
Related concepts
Related examples
Authoritative source
What it is
A thread calls method A which takes the lock, then calls method B which takes the same lock. With a plain lock, the thread blocks on itself forever. A reentrant lock recognizes the owner and succeeds, counting depth until the matching releases unwind.
How it works
The lock records owner identity and a recursion counter; acquire by the owner increments, unlock decrements, and release-to-others happens at zero. Fairness, try-lock with timeout, and condition variables attach as features in library implementations like ReentrantLock.
When to use it
Public APIs whose guarded methods call each other, frameworks invoking user overrides inside locked regions, and compatibility with legacy call shapes. Prefer narrowing lock scope so reentrancy is unnecessary when the option exists.
Pitfalls
Reentrancy hides accidental recursion: performance degrades and invariants blur while the code keeps working. Mixing reentrancy with condition waits — a condition release during wait — surprises depth accounting. Self-deadlock removal buys nothing against two-thread cycles; lock ordering discipline still applies.