교착 상태 (Deadlock)

한눈에 보기

둘 이상의 프로세스가 서로가 점유한 자원을 기다리며 영원히 멈추는 교착 상태를 다룬다. 네 가지 필요조건, 예방과 회피와 탐지 회복 전략, 기아와 라이브락의 구분, 데이터베이스 데드락 실무 사례까지 정리한다.

최종 업데이트 2026.09.17

교착 상태(Deadlock)는 두 개 이상의 프로세스가 서로가 점유한 자원을 기다리며 영원히 진행하지 못하는 상태다. 각 프로세스는 상대가 자원을 놓아야만 나아갈 수 있는데 아무도 놓지 않으므로 전체가 멈춘다. 운영체제와 데이터베이스, 멀티스레드 프로그램 어디에서나 발생할 수 있는 고전적인 동기화 문제다.

교착 상태가 성립하는 네 가지 조건

교착 상태는 다음 네 조건이 동시에 성립할 때만 발생한다. 하나라도 깨지면 교착 상태는 일어나지 않는다.

교착 상태(데드락)의 순환 대기: 프로세스 P1이 자원 R1을 쥔 채 R2를 기다리고 P2는 R2를 쥔 채 R1을 기다려 자원 할당 그래프에 사이클이 생긴 모습
네 조건 중 하나만 깨도 교착은 성립하지 않는다. 실무에서 가장 흔한 처방은 «순환 대기»를 깨는 것으로, 모든 코드가 락을 같은 순서(R1 → R2)로만 잡게 하면 이 그림의 사이클은 생길 수 없다.
  • 상호 배제(Mutual Exclusion): 자원을 한 번에 한 프로세스만 사용할 수 있다.
  • 점유와 대기(Hold and Wait): 자원을 쥔 채로 다른 자원을 추가로 기다린다.
  • 비선점(No Preemption): 자원을 강제로 빼앗을 수 없고 점유한 쪽이 스스로 놓아야 한다.
  • 순환 대기(Circular Wait): 프로세스들이 원형으로 서로의 자원을 기다린다.

자원 할당 그래프(Resource Allocation Graph)로 상황을 그렸을 때, 자원마다 인스턴스가 하나뿐이라면 그래프의 사이클(cycle)은 곧 교착 상태를 의미한다. 인스턴스가 여러 개라면 사이클은 교착 상태의 필요조건일 뿐 충분조건은 아니다.

전형적인 발생 예시

서로 다른 순서로 두 개의 락(lock)을 잡는 코드가 가장 흔한 사례다.

// 스레드 A                // 스레드 B
lock(m1);                  lock(m2);
lock(m2);  // B가 점유 중   lock(m1);  // A가 점유 중
...                        ...
unlock(m2); unlock(m1);    unlock(m1); unlock(m2);

스레드 A가 m1을 잡고 스레드 B가 m2를 잡은 직후라면, A는 m2를, B는 m1을 영원히 기다린다. 네 조건이 모두 갖춰진 상태다.

대처 전략 네 가지

전략방법비용과 한계
예방(Prevention)네 조건 중 하나를 구조적으로 깨뜨린다자원 이용률이 떨어지거나 프로그래밍 제약이 커진다
회피(Avoidance)은행원 알고리즘처럼 안전 상태를 유지하는 요청만 승인한다최대 자원 요구량을 미리 알아야 하고 매 요청마다 검사 비용이 든다
탐지와 회복(Detection and Recovery)주기적으로 사이클을 탐지하고 프로세스 강제 종료나 자원 회수로 푼다탐지 주기 동안 시스템이 멈춰 있을 수 있고 회복 시 작업 손실이 생긴다
무시(Ignore)발생 빈도가 낮다고 보고 아무것도 하지 않는다대부분의 범용 운영체제가 택하는 현실적 접근으로, 문제 시 재시작에 의존한다

예방 전략을 조건별로 보면, 점유와 대기는 필요한 자원을 시작 시점에 한꺼번에 요청하게 해서 깨고, 비선점은 추가 자원을 얻지 못하면 쥔 자원을 모두 내려놓게 해서 깬다. 순환 대기는 모든 자원에 전역 순서 번호를 매기고 반드시 오름차순으로만 요청하게 해서 깨는데, 실무에서 락 순서 규칙(lock ordering)으로 가장 널리 쓰인다.

개발자가 교착 상태를 가장 자주 만나는 곳은 데이터베이스다. 두 트랜잭션이 서로 다른 순서로 같은 행들의 락을 잡으면 교착 상태가 되는데, MySQL의 InnoDB 같은 엔진은 대기 그래프를 검사해 이를 즉시 탐지하고 한쪽 트랜잭션을 강제 롤백시킨 뒤 오류를 돌려준다. 즉 탐지와 회복 전략을 실제로 구현한 사례이며, 응용 코드는 이 오류를 받으면 트랜잭션을 재시도하도록 작성해야 한다. 멀티스레드 응용에서는 모든 스레드가 멈췄을 때 스레드 덤프(thread dump)를 떠서 각 스레드가 쥔 락과 기다리는 락을 확인하면 순환 대기를 눈으로 찾을 수 있다. 락을 잡은 채 외부 호출을 하지 않기, 락 범위를 최소화하기, 일정 시간 안에 락을 얻지 못하면 포기하는 타임아웃(timeout)을 두기 같은 습관도 실전에서 유효한 예방책이다.

은행원 알고리즘의 핵심 아이디어

회피 전략의 대표인 은행원 알고리즘(Banker's Algorithm)은 자원 요청을 승인했다고 가정한 뒤, 남은 자원으로 모든 프로세스가 최대 요구량까지 채우고 종료할 수 있는 순서가 하나라도 존재하는지 검사한다. 그런 순서가 있으면 안전 상태(safe state)이므로 승인하고, 없으면 요청을 보류한다. 안전 상태에 머무는 한 교착 상태로 들어가지 않는다는 것이 핵심이지만, 프로세스별 최대 요구량을 사전에 알아야 한다는 전제가 범용 시스템에서는 비현실적이라 주로 이론적 기준으로 다뤄진다.

교착 상태와 구별할 개념

기아(starvation)는 자원이 계속 다른 프로세스에 돌아가서 특정 프로세스만 무한정 기다리는 상황으로, 시스템 전체는 진행되고 있다는 점에서 교착 상태와 다르다. 라이브락(livelock)은 프로세스들이 서로 양보를 반복하며 상태는 계속 바뀌지만 실제 진전이 없는 상황이다. 좁은 복도에서 두 사람이 같은 방향으로 계속 비켜서는 모습에 비유된다. 셋 다 진행 불가라는 결과는 비슷하지만 원인과 해법이 다르므로 구분해서 설명할 수 있어야 한다. 예를 들어 기아는 스케줄링이나 락 획득의 공정성(fairness)을 보장해 해결하고, 라이브락은 재시도 전에 무작위 지연(random backoff)을 넣어 양보가 계속 겹치지 않게 해서 해결한다.

면접에서 이렇게 물어본다

  • 교착 상태의 네 가지 필요조건을 말하고 각각을 깨는 예방 방법을 하나씩 제시해 보라.
  • 교착 상태와 기아, 라이브락의 차이를 예를 들어 설명해 보라.
  • 멀티스레드 코드에서 락 두 개로 교착 상태가 나는 시나리오를 만들고 락 순서 규칙으로 고쳐 보라.
  • 은행원 알고리즘에서 안전 상태란 무엇이며 왜 실무에 그대로 적용하기 어려운가.
  • 대부분의 범용 운영체제가 교착 상태를 무시하는 전략을 택하는 이유는 무엇인가.
운영체제 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기