본문 바로가기
language/java

DeadLock(교착상태) 원인과 해결 완벽 이해하기

by 죄니안죄니 2026. 6. 1.
반응형

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 동시성 면접에서 최상급 난이도에 속한다.

반응형

댓글