JAVA 쓰레드 생명주기와 상태별 동작 원리 완벽 정리
🚦 쓰레드의 흐름을 이해하면 자바 동시성이 쉬워집니다
안녕하세요.
자바로 멀티스레드 프로그래밍을 하다 보면 꼭 마주치게 되는 개념이 바로 쓰레드의 생명주기입니다.
쓰레드가 생성되고 실행되는 시점은 쉽게 이해되지만, 일시정지나 종료 상태로 전환되는 과정은 처음 접하는 분들에겐 다소 복잡하게 느껴질 수 있어요.
실제로 많은 개발자들이 쓰레드 상태에 따라 어떤 메서드가 호출되는지를 제대로 이해하지 못해 동기화 오류나 교착상태를 경험하곤 하죠.
이번 포스팅에서는 자바 쓰레드의 생명주기를 전체 흐름으로 한눈에 파악하고, 각 상태에서 어떤 메서드가 어떤 역할을 하는지 쉽게 정리해 드릴게요.
처음 자바 쓰레드를 배우는 분들부터 실무에서 쓰레드를 다뤄야 하는 분들까지 모두에게 유용한 내용이니 꼭 끝까지 읽어보세요.
이번 글에서는 자바 쓰레드의 생명주기를 기준으로 각 상태가 의미하는 바와, 상태 전환에 영향을 주는 주요 메서드들을 체계적으로 정리합니다.
특히 동시성 처리의 흐름을 이해하는 데 중요한 Runnable, Blocked, Waiting 상태의 차이점을 명확히 구분해드릴 예정이에요.
글의 후반부에서는 실전 코드 예제도 함께 소개하니, 직접 실행하며 연습해보면 더 큰 도움이 될 거예요.
📋 목차
🧵 쓰레드란 무엇인가요?
자바에서 쓰레드(Thread)란 하나의 프로그램 내에서 동시에 실행될 수 있는 작업의 흐름을 의미합니다.
운영체제는 CPU의 시간을 여러 프로세스와 쓰레드에 분배해 빠르게 번갈아가며 실행되도록 만들기 때문에, 우리는 마치 여러 작업이 동시에 처리되는 것처럼 느낄 수 있습니다.
자바 프로그램은 기본적으로 하나의 메인 쓰레드를 가지고 실행되며, 개발자는 필요에 따라 추가로 쓰레드를 생성해 병렬 처리를 구현할 수 있습니다.
예를 들어, 백그라운드에서 파일을 다운로드하거나, 실시간 데이터를 처리해야 할 때 별도의 쓰레드를 활용하면 애플리케이션의 응답성을 유지할 수 있습니다.
💡 프로세스 vs 쓰레드의 차이
프로세스(Process)는 실행 중인 프로그램 자체이며, 운영체제로부터 독립된 메모리 공간을 할당받습니다.
반면 쓰레드는 해당 프로세스 내에서 실행되는 작업 단위로, 코드, 데이터, 힙 메모리는 공유하지만 스택 메모리는 각자 따로 갖고 있습니다.
- 🧩프로세스는 완전한 실행 단위이며 메모리를 독립적으로 소유합니다
- 🧵쓰레드는 하나의 프로세스 안에서 생성되며 자원을 공유합니다
- 🔄멀티쓰레드를 활용하면 동시에 여러 작업을 처리할 수 있습니다
이처럼 자바에서 쓰레드는 성능 최적화와 응답성 개선에 핵심적인 역할을 합니다.
하지만 잘못 사용하면 데이터 충돌이나 예기치 못한 버그를 유발할 수 있으니 생명주기와 상태 관리를 정확히 이해하는 것이 중요합니다.
🚀 쓰레드 생명주기 단계별 설명
자바의 쓰레드는 크게 5가지 상태로 구분되는 생명주기(life cycle)를 따릅니다.
각 상태는 자바의 Thread.State 열거형으로 정의되어 있으며, 개발자가 쓰레드를 제어할 수 있는 흐름의 단위를 의미합니다.
- 🍼NEW – 쓰레드가 생성되었지만 아직 시작되지 않은 상태
- 🏃RUNNABLE – 실행 준비가 되어 CPU를 기다리는 상태
- ⏸️WAITING / TIMED_WAITING – 다른 쓰레드의 작업을 기다리거나 일정 시간 정지된 상태
- 🚧BLOCKED – 동기화 자원을 기다리며 대기하는 상태
- 🛑TERMINATED – 쓰레드의 작업이 종료되어 더 이상 실행되지 않는 상태
🌀 상태 전이는 어떻게 일어날까요?
각 상태는 쓰레드가 실제로 호출하는 메서드에 따라 전환됩니다.
예를 들어 start()를 호출하면 NEW → RUNNABLE 상태로 전이되고, sleep(), join() 등은 TIMED_WAITING 또는 WAITING 상태를 유발합니다.
또한 synchronized 블록에 접근하려는 순간 다른 쓰레드가 해당 자원을 점유 중이면 BLOCKED 상태가 됩니다.
💬 쓰레드는 항상 RUNNABLE 상태에서 실행 대기 중이며, CPU가 할당되어야만 실제로 실행됩니다.
이처럼 자바에서 쓰레드의 생명주기는 정해진 흐름이 있으며, 각 상태별로 적절한 메서드 호출을 이해하는 것이 중요합니다.
다음 섹션에서는 상태 전환을 유발하는 핵심 메서드들을 하나씩 살펴보겠습니다.
🛠️ 상태 전환에 사용되는 주요 메서드
자바 쓰레드는 다양한 메서드를 통해 상태가 전환되며, 이 흐름을 명확히 이해해야 안정적인 멀티쓰레드 프로그래밍이 가능합니다.
아래는 상태 전환을 유도하는 주요 메서드들과 그 기능을 정리한 내용입니다.
| 메서드 | 설명 |
|---|---|
| start() | NEW → RUNNABLE로 전환하며 쓰레드 실행을 시작 |
| sleep(ms) | 지정 시간 동안 TIMED_WAITING 상태로 진입 |
| join() | 다른 쓰레드의 종료를 기다리며 WAITING 상태로 전환 |
| wait() | monitor lock을 해제하고 WAITING 상태에 진입 |
| notify() / notifyAll() | 대기 중인 쓰레드를 깨워 RUNNABLE로 전환 가능 |
| interrupt() | WAITING 또는 TIMED_WAITING 상태의 쓰레드를 깨움 |
💎 핵심 포인트:
start()는 단 한 번만 호출할 수 있으며, 이미 종료된 쓰레드는 다시 시작할 수 없습니다.
이 외에도 yield(), setDaemon(true) 같은 메서드들도 쓰레드 실행 방식에 영향을 줄 수 있습니다.
하지만 상태 전이를 직접 일으키는 것은 위 표에 정리된 메서드들이며, 대부분의 동시성 제어는 이들 중심으로 이뤄집니다.
🔄 쓰레드 상태 전이 흐름도
쓰레드의 상태 전이는 정해진 생명주기 순서에 따라 흐릅니다.
이를 도식화하면 쓰레드가 어떤 메서드 호출을 통해 어느 상태로 이동하는지를 한눈에 이해할 수 있어요.
🗺️ 상태 전이 흐름 요약
- 🍼NEW 상태에서
start()를 호출하면 RUNNABLE 상태로 전환 - 🏃RUNNABLE은 실제 실행 중이거나 실행 대기 중인 상태
- ⏳
sleep(), join(), wait()등 호출 시 WAITING 또는 TIMED_WAITING 상태로 이동 - 🚧
synchronized진입 실패 시 BLOCKED 상태로 전이 - 🛑run() 메서드 종료 또는 예외 발생 시 TERMINATED 상태로 진입
💎 핵심 포인트:
쓰레드는 BLOCKED와 WAITING을 명확히 구분해야 합니다. BLOCKED는 lock 확보 대기, WAITING은 다른 쓰레드의 동작을 기다리는 상태입니다.
이처럼 쓰레드 상태는 단순히 실행/정지의 개념을 넘어서, 다양한 비동기 흐름 속에서의 논리적 대기 상태를 표현합니다.
실제 코드를 작성할 때 이 흐름을 잘 이해하고 설계해야 교착상태(deadlock)나 불필요한 성능 저하를 방지할 수 있어요.
💻 실습 예제: Thread 상태 추적
쓰레드 생명주기를 제대로 이해하려면 직접 코드를 실행해보는 것이 가장 좋습니다.
아래는 자바의 Thread 상태 변화를 출력하는 간단한 실습 예제입니다.
이 코드를 통해 상태가 어떻게 바뀌는지 콘솔에서 확인할 수 있어요.
public class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("1. 상태: " + t.getState()); // NEW
t.start();
System.out.println("2. 상태: " + t.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println("3. 상태: " + t.getState()); // TIMED_WAITING
t.join();
System.out.println("4. 상태: " + t.getState()); // TERMINATED
}
}
💎 핵심 포인트:
getState() 메서드를 사용하면 실행 중인 쓰레드의 현재 상태를 실시간으로 확인할 수 있습니다.
실제로 위 코드처럼 Thread.sleep()과 join()을 적절히 조합하면 RUNNABLE → TIMED_WAITING → TERMINATED 흐름을 쉽게 추적할 수 있어요.
이런 연습을 통해 생명주기의 개념이 자연스럽게 체득됩니다.
추가로, BLOCKED 상태는 synchronized 블록에 여러 쓰레드가 동시에 접근할 때 테스트해볼 수 있어요.
별도의 연습 예제도 직접 구성해보면 쓰레드의 작동 방식이 훨씬 더 명확해질 거예요.
❓ 자주 묻는 질문 (FAQ)
Thread의 start()와 run()의 차이점은 무엇인가요?
쓰레드 상태 중 BLOCKED와 WAITING은 어떻게 다른가요?
TIMED_WAITING 상태는 어떤 메서드에서 발생하나요?
쓰레드 상태를 확인할 수 있는 방법은?
하나의 쓰레드에서 여러 번 start()를 호출할 수 있나요?
쓰레드가 TERMINATED 상태가 되는 조건은?
쓰레드를 안전하게 종료하는 방법은 무엇인가요?
쓰레드에서 동기화가 필요한 이유는?
🧠 쓰레드 생명주기를 이해하면 동시성이 보인다
이번 글에서는 자바에서 가장 기본이자 핵심 개념 중 하나인 쓰레드 생명주기에 대해 단계별로 알아보았습니다.
쓰레드는 NEW → RUNNABLE → WAITING/BLOCKED → TERMINATED 순으로 흐르며, 각 상태는 특정 메서드에 의해 전이됩니다.
이 흐름을 이해하면 동시성 프로그래밍에서 흔히 발생하는 교착상태나 응답 지연 문제를 방지할 수 있습니다.
또한 실전 코드 예제를 통해 getState()로 상태를 추적하는 방법도 익혔습니다.
이처럼 직접 실행해보며 연습하면 쓰레드가 눈에 보이듯 익숙해질 거예요.
자바 멀티쓰레드 프로그래밍을 본격적으로 시작하고자 한다면, 쓰레드의 생명주기를 정확히 이해하고 상태 전이를 자연스럽게 떠올릴 수 있어야 합니다.
그만큼 중요한 주제이니 꼭 여러 번 반복해서 학습해보세요.
🏷️ 관련 태그 : 자바쓰레드, 자바멀티스레딩, Thread생명주기, 동시성처리, synchronized, wait notify, 자바코딩기초, 자바입문, JavaThread, 상태전이