전체 목록
운영체제Medium#083

데드락(Deadlock)이란 무엇이며, 발생 조건 4가지와 해결 방법을 설명해주세요.

#OS#데드락#프로세스#동기화

답변 포인트

Coffman 조건 4가지를 떠올려보세요.

정답 및 해설

빠른 요약

데드락은 둘 이상의 프로세스가 서로가 점유한 자원을 기다리며 무한히 대기하는 상태입니다. 발생 조건(Coffman 조건) 4가지: 1.

데드락(Deadlock)은 둘 이상의 프로세스(또는 스레드)가 서로가 점유하고 있는 자원을 기다리며 영원히 대기하는 상태를 말합니다. 어떤 프로세스도 앞으로 진행할 수 없는 교착 상태이므로 시스템 전체가 멈추게 됩니다. 운영체제, 데이터베이스, 멀티스레드 프로그래밍에서 모두 발생할 수 있는 중요한 개념입니다.

데드락 발생 상황 예시

운영체제
 A:  1 ,  2 
 B:  2 ,  1 

:
                 
  A  >    1  
                     
                                        
                                     
                                        
                  
   2   <   B 
                     

 A  2 , B  1 
    = !

데드락 발생 조건 (Coffman 조건)

1969년 Coffman이 제시한 4가지 조건이 모두 동시에 만족될 때 데드락이 발생합니다. 하나라도 없으면 데드락이 발생하지 않습니다.

1. 상호 배제 (Mutual Exclusion)

하나의 자원은 한 번에 오직 하나의 프로세스만 사용할 수 있습니다.

운영체제
 R1:  A  
           B A     

: ,   , (Mutex)

2. 점유와 대기 (Hold and Wait)

프로세스가 최소 하나의 자원을 보유한 채로 다른 프로세스가 점유 중인 자원을 기다립니다.

운영체제
 A:  R1  (Hold) +  R2  (Wait)

:      

3. 비선점 (Non-Preemption)

프로세스가 보유한 자원을 강제로 빼앗을 수 없으며, 자원은 오직 보유한 프로세스가 자발적으로 해제할 때만 반환됩니다.

운영체제
 A  R1  
 OS  B  R1   
 A     

4. 순환 대기 (Circular Wait)

프로세스들이 원형(환형)으로 서로의 자원을 기다립니다.

운영체제
P1  P2  P3  P1 ()

P1: P2   
P2: P3   
P3: P1   

   (Cycle) 

데드락 해결 방법

데드락 처리 방법은 크게 4가지로 분류됩니다.

1. 예방 (Prevention): Coffman 조건 중 하나 제거

운영체제
 1 :   

-    
-      
-       

 2 :   

-  1:    ,     
    , (Starvation)  
-  2:        

 3 :  

-      ,     
- CPU ()  
-      

 4 :    ( )

-   ,    
-   

순환 대기 제거 코드 예시

Java
// 잘못된 방법 - 데드락 발생 가능
public class DeadlockExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    // 스레드 A
    public void methodA() {
        synchronized (lock1) {        // lock1 획득
            synchronized (lock2) {    // lock2 획득
                // 작업
            }
        }
    }

    // 스레드 B - lock 순서가 반대!
    public void methodB() {
        synchronized (lock2) {        // lock2 획득
            synchronized (lock1) {    // lock1 획득 (A가 보유 중이면 데드락!)
                // 작업
            }
        }
    }
}

// 올바른 방법 - 항상 같은 순서로 락 획득
public class DeadlockFreeExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    // 스레드 A
    public void methodA() {
        synchronized (lock1) {
            synchronized (lock2) { /* 작업 */ }
        }
    }

    // 스레드 B - 동일한 순서로 락 획득
    public void methodB() {
        synchronized (lock1) {  // 항상 lock1 먼저!
            synchronized (lock2) { /* 작업 */ }
        }
    }
}

2. 회피 (Avoidance): 은행원 알고리즘

시스템이 항상 **안전 상태(Safe State)**를 유지하도록 자원을 할당합니다.

운영체제
  (Banker's Algorithm):
-      
-        
-  :         

:
 : 12
  :
  P1:  5,  9    : 4
  P2:  2,  3    : 1
  P3:  2,  7    : 5
 : 12 - 5 - 2 - 2 = 3

 : P2(1 )  P1(4 )  P3(5 )
  ,   
Python
# 은행원 알고리즘 (Python 의사 코드)
def is_safe_state(available, max_need, allocation):
    n_processes = len(allocation)
    n_resources = len(available)

    work = available.copy()
    finish = [False] * n_processes
    need = [[max_need[i][j] - allocation[i][j]
             for j in range(n_resources)]
             for i in range(n_processes)]

    # 안전 시퀀스 찾기
    safe_sequence = []
    while len(safe_sequence) < n_processes:
        found = False
        for i in range(n_processes):
            if not finish[i] and all(need[i][j] <= work[j] for j in range(n_resources)):
                # Pi의 자원을 모두 해제
                for j in range(n_resources):
                    work[j] += allocation[i][j]
                finish[i] = True
                safe_sequence.append(i)
                found = True
                break

        if not found:
            return False, []  # 교착 상태 발생 가능

    return True, safe_sequence

3. 탐지 (Detection) + 복구 (Recovery)

데드락을 허용하되, 발생 후 탐지하고 복구합니다.

운영체제
 :
1.   (Resource Allocation Graph) 
2.    
3.        

 :
1.  
   -      ( ,  )
   -     (  )

2.    (Preemption)
   -       
   -      
   - (Starvation)  

4. 무시 (Ostrich Algorithm)

데드락 발생 빈도가 매우 낮은 경우, 처리 비용이 이득보다 클 때 무시합니다.

운영체제
- UNIX/Linux    OS 
-    
-    

데이터베이스 데드락

발생 시나리오

SQL
-- 트랜잭션 A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 행 1 락 획득
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 행 2 락 대기

-- 트랜잭션 B (동시 실행)
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 2;  -- 행 2 락 획득
UPDATE accounts SET balance = balance + 50 WHERE id = 1;  -- 행 1 락 대기

-- 결과: A는 B를 기다리고, B는 A를 기다림 → 데드락

DB 데드락 해결 방법

Java
// 방법 1: 락 순서 고정
@Transactional
public void transfer(Long fromId, Long toId, int amount) {
    // 항상 작은 ID의 계좌를 먼저 락
    Long firstId = Math.min(fromId, toId);
    Long secondId = Math.max(fromId, toId);

    Account first = accountRepository.findByIdWithLock(firstId);   // SELECT FOR UPDATE
    Account second = accountRepository.findByIdWithLock(secondId); // SELECT FOR UPDATE

    // ... 이체 처리
}

// 방법 2: 타임아웃 + 재시도
@Retryable(
    value = { DeadlockLoserDataAccessException.class },
    maxAttempts = 3,
    backoff = @Backoff(delay = 100, multiplier = 2)
)
@Transactional
public void processOrder(Long orderId) {
    // 데드락 발생 시 최대 3번 재시도
}

// 방법 3: 낙관적 락 (버전 기반)
@Entity
public class Account {
    @Id
    private Long id;

    private int balance;

    @Version  // 버전 컬럼 추가
    private int version;
}

// 충돌 시 OptimisticLockingFailureException 발생 → 재시도 처리

Java 멀티스레드 데드락 탐지

Java
// Java에서 데드락 탐지 방법
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector {

    public static void detectDeadlock() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();

        if (deadlockedThreads != null) {
            ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
            System.out.println("데드락 탐지!");
            for (ThreadInfo info : threadInfos) {
                System.out.println("스레드: " + info.getThreadName());
                System.out.println("락 대기: " + info.getLockName());
                System.out.println("락 보유자: " + info.getLockOwnerName());
            }
        }
    }
}

// java.util.concurrent.locks.ReentrantLock + tryLock 타임아웃
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();

public boolean transfer() throws InterruptedException {
    if (lock1.tryLock(1, TimeUnit.SECONDS)) {     // 1초 타임아웃
        try {
            if (lock2.tryLock(1, TimeUnit.SECONDS)) { // 1초 타임아웃
                try {
                    // 작업 수행
                    return true;
                } finally {
                    lock2.unlock();
                }
            }
        } finally {
            lock1.unlock();
        }
    }
    return false; // 타임아웃 발생 → 재시도 가능
}

요약 표

Coffman 조건설명예방 방법
상호 배제자원 독점 사용자원 공유 허용 (읽기 전용 등)
점유와 대기자원 보유 + 다른 자원 대기모든 자원 한꺼번에 요청
비선점자원 강제 회수 불가자원 반환 후 재요청
순환 대기프로세스 간 환형 대기자원 번호 순서대로 요청
해결 전략방법장점단점
예방Coffman 조건 1개 제거데드락 원천 차단자원 활용도 저하, 기아 가능
회피은행원 알고리즘자원 효율적 활용최대 자원량 사전 파악 필요
탐지+복구그래프 분석 + 강제 종료자원 활용도 높음복구 비용, 작업 손실 가능
무시데드락 방치구현 비용 없음시스템 중단 가능

관련 질문

같은 카테고리/태그 기준