The 30 second answer
You can get a deadlock: each thread holds one lock and waits forever for the other. It is timing-dependent, so it often passes tests and fails in production. There is no exception; the threads sit blocked. I diagnose with a thread dump looking for a Java-level deadlock, and I fix it with a global lock order, tryLock with a timeout and back-off, or by collapsing to a single lock.
Possible deadlock: each holds one lock and waits forever for the other (not guaranteed every run — timing-dependent, which is why it escapes tests). No exception; threads sit BLOCKED. Diagnose with jstack/thread dump ("Found one Java-level deadlock"). Fix: global lock ordering (always A before B, e.g. order by account id), ReentrantLock.tryLock(timeout) with back-off, or reduce to one lock.
if (lockA.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
if (lockB.tryLock(50, TimeUnit.MILLISECONDS)) {
try { /* critical section */ }
finally { lockB.unlock(); }
}
} finally { lockA.unlock(); }
}Sources: