Deadlocks: When Safety Itself Freezes the System
“Deadlocks happen not because code is unsafe, but because it is too safe in the wrong order.”
Yesterday you learned how to lock correctly.
Today you learn what happens when locks fight each other.1️⃣ What Is a Deadlock (Plain English)
A deadlock happens when:
- Two or more transactions hold locks
- Each is waiting for the other to release a lock
- None can proceed
➡️ System freezes, not crashes.
2️⃣ The Classic Deadlock Example (Bank Transfer)
Two accounts:
A, B
Two concurrent transfers:
Transaction T1
LOCK A
LOCK B (waits)
Transaction T2
LOCK B
LOCK A (waits)
? Both wait forever → deadlock
3️⃣ Why Deadlocks Appear Only in Production
- More concurrency
- More transactions
- Longer transactions
- Slower DB responses
Development rarely creates the timing needed.
4️⃣ Conditions Required for Deadlock (MEMORIZE)
All four must exist:
1️⃣ Mutual exclusion (locks)
2️⃣ Hold and wait
3️⃣ No preemption (can’t force release)
4️⃣ Circular wait
Remove any one → no deadlock.
5️⃣ How Databases Handle Deadlocks
Good news:
Databases detect deadlocks automatically
What happens:
- DB picks a victim transaction
- Rolls it back
- Other transaction proceeds
Bad news:
- If your code isn’t retry-safe → user sees error
6️⃣ The #1 Rule to Avoid Deadlocks (MOST IMPORTANT)
Always acquire locks in the SAME ORDER
❌ Bad
A → B
B → A
✅ Good
A → B
A → B
In code:
if (fromId < toId) {
lock(from);
lock(to);
} else {
lock(to);
lock(from);
}
7️⃣ Keep Transactions SHORT
Long transactions:
- Hold locks longer
- Increase deadlock chance
❌ Bad
@Transactional
public void process() {
lock();
callExternalAPI(); // ?
update();
}
✅ Good
callExternalAPI();
@Transactional
public void process() {
lock();
update();
}
8️⃣ Atomic Updates Reduce Deadlocks
This:
UPDATE account
SET balance = balance - 100
WHERE id = 1 AND balance >= 100;
Is deadlock-free
(no read lock + write lock sequence)
9️⃣ Retry Strategy (IMPORTANT)
Deadlocks are expected.
Safe retry:
- Short delay
- Limited retries
- Only for idempotent operations
@Retryable(
value = DeadlockLoserDataAccessException.class,
maxAttempts = 3
)
? What NOT to Do ❌
| Mistake | Why |
|---|---|
| Ignore deadlock errors | Users see failures |
| Long transactions | Lock explosion |
| Random lock order | Guaranteed deadlocks |
| Retrying non-idempotent ops | Data corruption |
? Mental Checklist (Before Writing Transactional Code)
Ask:
- What rows will I lock?
- In what order?
- How long will I hold them?
- What happens if DB kills my transaction?
If you can’t answer → redesign.
? One-Sentence Rule
Deadlocks are a sign of correct locking without correct ordering.
? Day 5 Preview
Isolation Levels — How Much Reality Your Transaction Sees
You’ll learn:
- READ COMMITTED vs SERIALIZABLE
- Phantom reads
- Why higher isolation ≠ always better