DeadLock(교착상태) 원인과 해결 완벽 이해하기
멀티스레드에서 가장 무서운 문제 중 하나가 바로:
DeadLock (교착상태)
이다.
실무에서는 CPU도 안 쓰고,
에러도 안 나고,
로그도 안 찍히는데
서비스가 멈춘 것처럼 보이는 장애가 발생할 수 있다.
알고 보면 대부분 DeadLock 때문이다.
1. DeadLock이란?
한 줄 정의.
서로가 가진 자원을 기다리면서
영원히 진행하지 못하는 상태
2. 비유
화장실 열쇠 2개가 있다고 가정.
사람 A
열쇠 1 보유
↓
열쇠 2 기다림
사람 B
열쇠 2 보유
↓
열쇠 1 기다림
결과
A 대기
B 대기
영원히 대기
3. Java 예제
락 2개.
private static final Object lock1 =
new Object();
private static final Object lock2 =
new Object();
Thread A
synchronized(lock1) {
Thread.sleep(100);
synchronized(lock2) {
}
}
4. Thread B
synchronized(lock2) {
Thread.sleep(100);
synchronized(lock1) {
}
}
5. 실행 순서
Thread A
lock1 획득
Thread B
lock2 획득
Thread A
lock2 대기
Thread B
lock1 대기
결과
무한 대기
6. 그림
Thread A
lock1 보유
↓
lock2 대기
----------------
Thread B
lock2 보유
↓
lock1 대기
7. 이것이 DeadLock
Deadlock
8. 왜 위험할까?
예외 없음.
CPU 사용량 낮음.
로그 없음.
그냥 멈춘 것처럼 보임.
9. 실무 장애 특징
API 응답 없음
Thread 증가
CPU 정상
DB 정상
알고 보니 DeadLock.
10. DeadLock 발생 조건
매우 중요.
면접 단골.
DeadLock은 아래 4가지 조건이 모두 만족해야 발생.
11. 상호 배제 (Mutual Exclusion)
한 번에 한 Thread만 사용 가능
Lock 자체가 해당.
12. 점유와 대기 (Hold And Wait)
이미 Lock 보유
+
추가 Lock 대기
예:
lock1 보유
↓
lock2 대기
13. 비선점 (No Preemption)
강제로 뺏을 수 없음
Lock 가진 Thread만 해제 가능.
14. 순환 대기 (Circular Wait)
A → B 기다림
B → A 기다림
DeadLock 핵심.
15. 그림
Thread A
↓
Lock B
↓
Thread B
↓
Lock A
↓
Thread A
원형 구조.
16. DeadLock 발생 예
실무에서 많이 나오는 패턴.
transfer(A, B);
transfer(B, A);
동시 실행.
17. 계좌 이체 예제
synchronized(accountA) {
synchronized(accountB) {
}
}
다른 Thread
synchronized(accountB) {
synchronized(accountA) {
}
}
DeadLock 가능.
18. 가장 쉬운 해결 방법
락 순서 통일.
19. 나쁜 예
Thread A
lock1 → lock2
Thread B
lock2 → lock1
20. 좋은 예
모든 코드.
lock1 → lock2
순서 강제.
21. 결과
순환 대기 제거
DeadLock 불가.
22. 실무에서 가장 많이 사용하는 방법
락 획득 순서 통일.
예:
if(id1 < id2)
first = account1;
second = account2;
항상 작은 ID 먼저 Lock.
23. ReentrantLock 활용
이전 글과 연결.
lock.tryLock()
사용.
24. 예시
if(lock1.tryLock()) {
try {
if(lock2.tryLock()) {
try {
work();
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
25. 장점
못 얻으면 포기
가능.
DeadLock 방지.
26. Timeout 사용
실무 중요.
lock.tryLock(
3,
TimeUnit.SECONDS
);
의미.
3초 기다림
실패 시 포기
27. DeadLock 회피
무한 대기
↓
유한 대기
로 변경.
28. Lock 범위 최소화
좋은 방법.
나쁜 예.
synchronized(lock) {
DB 조회
API 호출
파일 저장
}
29. 문제
Lock 오래 점유.
DeadLock 확률 증가.
30. 좋은 예
DB 조회
API 호출
synchronized(lock) {
count++;
}
최소 범위만 Lock.
31. Nested Lock 줄이기
나쁜 예.
lock1
↓
lock2
↓
lock3
↓
lock4
락 많을수록 위험.
32. 가능하면
락 개수 최소화
33. Thread Dump 분석
실무 핵심.
서버 멈춤.
↓
Thread Dump 확인.
JDK 제공 도구.
jstack <PID>
옵션 설명:
jstack
=
JVM Thread 상태 출력 도구
PID
=
Java 프로세스 ID
34. DeadLock 발견
예:
Found one Java-level deadlock:
Thread Dump에 표시.
35. 예시
Thread-1
waiting to lock
lock2
Thread-2
waiting to lock
lock1
바로 확인 가능.
36. VisualVM
VisualVM
GUI로 확인 가능.
Thread 탭.
↓
DeadLock 감지.
37. jconsole
JConsole
DeadLock 자동 탐지 지원.
38. 실무 면접 단골 질문
Q. DeadLock이란?
서로가 가진 자원을 기다리며
영원히 진행하지 못하는 상태
Q. 발생 조건 4가지는?
Mutual Exclusion
Hold And Wait
No Preemption
Circular Wait
Q. 가장 쉬운 해결 방법은?
Lock 획득 순서 통일
Q. ReentrantLock으로 방지 가능?
tryLock
timeout
사용 가능
Q. DeadLock 발견 방법은?
jstack
VisualVM
JConsole
39. synchronized vs ReentrantLock 관점
synchronized
무한 대기 가능
ReentrantLock
tryLock
timeout
지원.
DeadLock 회피 쉬움.
40. 실무에서 기억할 것
DeadLock은
Lock이 많아질수록
확률 증가.
특히:
계좌 이체
재고 차감
주문 처리
분산 락
에서 자주 발생.
41. 핵심 흐름 요약
Thread A
↓
Lock1 획득
↓
Lock2 대기
----------------
Thread B
↓
Lock2 획득
↓
Lock1 대기
----------------
DeadLock 발생
42. 가장 중요한 핵심 한 줄
DeadLock은 여러 스레드가 서로가 보유한 Lock을 기다하며 영원히 진행하지 못하는 상태이며, 실무에서는 Lock 획득 순서 통일과 tryLock(timeout)을 통해 예방하는 것이 가장 중요하다.
43. 다음 글 예고
다음 글은:
ConcurrentHashMap 동시성 처리
이다.
앞에서 Thread-safe 컬렉션에서 사용법은 봤지만,
이번에는 내부 구현 관점에서:
HashMap
↓
ConcurrentHashMap
↓
CAS
↓
synchronized
↓
Node
↓
TreeBin
까지 JDK 8 기준 구조를 깊게 분석하게 된다.
이 주제는 Java 동시성 면접에서 최상급 난이도에 속한다.
'language > java' 카테고리의 다른 글
| 병렬 Stream(parallelStream) 주의점 완벽 이해하기 (0) | 2026.06.01 |
|---|---|
| ConcurrentHashMap 동시성 처리 완벽 이해하기 (0) | 2026.06.01 |
| ReentrantLock 완벽 이해하기 (0) | 2026.06.01 |
| ForkJoinPool 완벽 이해하기 (0) | 2026.06.01 |
| CompletableFuture 완벽 이해하기 (0) | 2026.06.01 |
댓글