tryLock() attempts to acquire the lock without blocking: it returns true if it got the lock and false immediately if another thread holds it. The timed form tryLock(t, unit) waits up to a bound. This lets a thread give up instead of waiting forever — the key to lock-ordering-free deadlock avoidance.
Why it helps with deadlock
Deadlock needs all four Coffman conditions, including hold-and-wait (hold one lock while blocking on another). tryLock breaks it: if you can't get the second lock, you release the first, back off, and retry. No thread ever blocks indefinitely while holding a resource another thread needs.
boolean transfer(Account from, Account to, long amt) {
while (true) {
if (from.lock.tryLock()) {
try {
if (to.lock.tryLock()) { // don't block holding from.lock
try {
from.balance -= amt;
to.balance += amt;
return true;
} finally { to.lock.unlock(); }
}
} finally { from.lock.unlock(); } // release on failed inner lock
}
// both attempts handled; back off to avoid livelock
Thread.sleep(ThreadLocalRandom.current().nextLong(1, 10));
}
}
Note each acquired lock is unlocked in its own finally, and the first lock is released before retrying so the next iteration starts clean.
Two subtleties
- Untimed
tryLock()ignores fairness. Even on a fair lock, the no-argtryLock()barges — it grabs the lock if free, jumping the queue. Use the timedtryLock(0, unit)if you want fairness honored. - Add randomized back-off. A tight retry loop where threads keep grabbing and releasing in lock-step is a livelock — everyone is busy, nobody progresses. Randomized jitter between retries breaks the symmetry.