본문 바로가기
Language/Java

[JAVA] 자바 쓰레드 제어의 한 끗 차이 : sleep() vs wait() 완벽 분석

by Papa Martino V 2026. 1. 21.
728x90

sleep() vs wait()
sleep() vs wait()

 

자바 멀티쓰레딩 환경에서 쓰레드를 일시적으로 정지시켜야 하는 상황은 매우 빈번하게 발생합니다. 이때 개발자들이 가장 많이 혼동하는 두 가지 메서드가 바로 Thread.sleep()Object.wait()입니다. 두 메서드 모두 쓰레드를 '대기' 상태로 만든다는 점에서는 비슷해 보이지만, 그 내부 동작 원리와 락(Lock)의 소유권 처리 방식은 완전히 상반됩니다. 이 차이를 정확히 이해하지 못하고 사용하면, 멀티쓰레드 애플리케이션에서 심각한 성능 저하나 데드락(Deadlock) 문제를 야기할 수 있습니다. 본 포스팅에서는 전문가의 시각에서 두 메서드의 메커니즘을 심층 비교하고, 실무에서 어떤 상황에 어떤 도구를 선택해야 하는지 명확한 가이드를 제시합니다.

1. 핵심 차이점 요약

sleep()wait()의 가장 결정적인 차이는 "잠들 때 락을 들고 있느냐, 내려놓느냐"에 있습니다. sleep()은 단순히 현재 쓰레드의 실행을 멈추는 기능에 집중하는 반면, wait()은 쓰레드 간의 유기적인 협력을 위해 설계되었습니다.

비교 항목 Thread.sleep() Object.wait()
정의 위치 java.lang.Thread (정적 메서드) java.lang.Object (인스턴스 메서드)
락(Lock) 처리 락을 유지한 채 잠듦 락을 해제하고 대기 상태 진입
깨어나는 조건 지정한 시간이 경과하면 자동 복귀 notify() 또는 notifyAll() 호출 시
사용 위치 어디서든 사용 가능 synchronized 블록/메서드 내에서만 가능
사용 목적 단순 시간 지연, 폴링 간격 조절 쓰레드 간 상태 공유 및 협력 작업

2. Deep Dive: 왜 wait()은 락을 해제해야만 할까?

wait() 메서드가 호출되면 해당 쓰레드는 '대기실(Wait Set)'로 들어가면서 자신이 점유하고 있던 객체의 모니터 락을 반납합니다. 그래야만 대기 중이던 다른 쓰레드가 해당 객체에 접근하여 작업을 수행하고, 나중에 notify()를 통해 잠든 쓰레드를 깨워줄 수 있기 때문입니다. 반면 sleep()은 쓰레드 혼자 잠시 쉬는 것이기 때문에 다른 쓰레드와 상호작용할 필요가 없어 락을 굳게 쥐고 있습니다.


3. 실무 예제 (Sample Example)

다음은 두 메서드의 동작 차이를 명확히 보여주는 예제 코드입니다.

public class SyncTest {
    private final Object lock = new Object();

    public void testSleep() {
        synchronized(lock) {
            try {
                System.out.println("Sleep 시작 (락 유지)");
                Thread.sleep(2000); // 이 동안 다른 쓰레드는 lock 접근 불가
                System.out.println("Sleep 종료");
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }

    public void testWait() {
        synchronized(lock) {
            try {
                System.out.println("Wait 시작 (락 반납)");
                lock.wait(); // 다른 쓰레드가 lock을 얻어 notify() 해줄 때까지 대기
                System.out.println("Wait 종료");
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

4. 상황별 권장 사용 가이드

  • 특정 주기로 작업을 반복해야 할 때: sleep()을 사용하세요. 예를 들어 5초마다 로그를 확인하는 배치 작업 등입니다.
  • 자원이 준비될 때까지 기다려야 할 때: wait()을 사용하세요. 생산자가 데이터를 넣을 때까지 소비자가 기다리는 '생산자-소비자' 패턴이 대표적입니다.
  • 데드락 방지: synchronized 블록 내에서 sleep()을 사용할 때는 매우 주의해야 합니다. 락을 쥐고 잠들기 때문에 다른 쓰레드가 영원히 대기 상태에 빠질 수 있습니다.

내용 출처 및 참고 문헌

  • Java Platform, Standard Edition 21 API Specification - java.lang.Thread / Object
  • Effective Java 3rd Edition (Joshua Bloch) - Item 81: wait와 notify보다는 동기화 장치를 사용하라
  • Java Concurrency in Practice (Brian Goetz)
728x90