개요
해당 게시글에 앞서 아래 포스팅을 읽고 오시면 이해하는데 도움이 되실 것입니다.
https://jaimemin.tistory.com/2357
자바 쓰레드 개요 및 생성 방법
자바 쓰레드는 JVM에서 User Thread를 생성할 때 Kernel Thread와 1:1로 매핑되어 최종적으로 커널에서 관리됩니다.
- JVM에서 쓰레드를 생성할 때마다 Java Native Interface(JNI)를 통해 커널 영역을 호출하여 자바 쓰레드와 대응하는 커널 쓰레드를 생성 후 매핑
자바 쓰레드 생성 방법은 아래와 같이 크게 두 가지가 있습니다.
- Thread 클래스 상속하는 방법
- Runnable 인터페이스를 구현하는 방법
1. Thread 클래스 상속하는 방법
해당 방법은 작업 내용을 쓰레드 내부에 직접 재정의 후 실행하며 과정은 아래와 같습니다.
- Thread 클래스 상속
- run() 메서드 재정의
- start() 메서드 실행
장점
- Thread 관련 기능이 필요할 경우 유용
- Thread 익명 클래스 사용 가능
단점
- 별도의 클래스를 생성해야하기 때문에 번거로움
- 자바에서는 다중 상속이 불가능하므로 Thread 클래스를 상속받을 경우 다른 클래스를 상속받을 수 없음
- Thread 클래스에 구현된 코드들에 의해 메모리와 시간 등과 같은 추가 자원이 필요
- 상속의 특성상 컴파일 시점에 실행 코드가 결정되어 동적인 기능 변경이 불가능
이처럼 장점보단 단점이 훨씬 많기 때문에 보통은 쓰레드를 생성할 때 두 번째 방법을 채택합니다.
2. Runnable 인터페이스를 구현하는 방법
해당 방법은 작업 내용을 Runnable에 정의해서 쓰레드에 전달 후 쓰레드가 Runnable을 실행하는 구조이며 과정은 아래와 같습니다.
- Runnable 인터페이스 구현
- run() 메서드 재정의
- Thread 생성 시 생성자에 전달
- start() 메서드 실행
장점
- 익명 객체 및 람다 사용 가능
- 쓰레드와 작업을 분리하여 좀 더 유연하고 확장 가능한 구조로 구현 가능
- 코드가 간결
- 자원 사용량 적음
단점
- Thread 관련 기능 확장 필요시 해당 방법 사용 불가
자바 쓰레드 실행 및 종료
자바 쓰레드는 OS 스케줄러에 의해 실행 순서가 결정되며 커널이 주체이기 때문에 쓰레드 실행 시점을 JVM에서 제어할 수 없습니다.
새로운 쓰레드는 현재 쓰레드와 독립적으로 실행되고 최대 한번 시작할 수 있고 한번 종료된 쓰레드는 재시작이 불가능합니다.
1. 쓰레드 실행 관련 메서드
쓰레드를 실행 관련 메서드는 start()과 run()이 있습니다.
start()
- 쓰레드를 실행시키는 메서드
- 시스템 콜을 통해 커널에 커널 쓰레드 생성 요청
run()
- 쓰레드가 실행되면 해당 쓰레드에 의해 자동을 호출되는 메서드
- Thread의 run() 메서드가 자동 호출되고 여기서 Runnable 구현체가 존재할 경우 Runable의 run()을 실행
- public static void main(String[] args)가 메일 쓰레드에 의해 자동으로 호출되는 것과 비슷한 원리
2. 쓰레드 실행 과정
쓰레드를 실행시키는 과정은 아래와 같습니다.
- 메인 쓰레드가 새로운 쓰레드를 생성
- 메인 쓰레드가 start() 메서드를 호출해 쓰레드 실행
- 내부적으로 native 메서드인 start0()를 호출해 커널에서 커널 쓰레드를 생성하도록 시스템 콜 호출
- 커널 쓰레드가 생성되고 자바 쓰레드와 커널 쓰레드가 1:1 매핑
- 커널 쓰레드는 OS 스케줄러로부터 CPU 할당을 받기까지 실행대기 상태
- 커널 쓰레드가 스케줄러에 의해 실행 상태가 되면 JVM에서 매핑된 자바 쓰레드의 run() 메서드 호출
* 주의: start()가 아닌 run() 메서드를 직접 호출할 경우 새로운 쓰레드가 생성되지 않고 직접 호출한 쓰레드의 실행 스택에서 run()이 실행됩니다.
3. 쓰레드 종료 및 애플리케이션 종료
쓰레드는 run() 메서드가 모두 실행되거나 예외가 발생할 경우 자동으로 종료가 됩니다.
- 예외 발생하더라도 타 쓰레드 영향 X
애플리케이션 종료는 쓰레드가 싱글이냐 멀티냐에 따라 종료 기준이 다릅니다.
싱글 쓰레드 애플리케이션
- 기본 main thread 종료 시 애플리케이션 종료
멀티 쓰레드 애플리케이션
- JVM에서 실행하고 있는 모든 쓰레드가 종료되어야 어플리케이션 종료
쓰레드 생명주기
쓰레드를 효과적으로 운용하기 위해서는 쓰레드 생명주기와 상태를 숙지해야 합니다.
자바 쓰레드는 생성과 실행 그리고 종료에 따른 상태를 가지며 JVM에서는 아래의 6가지의 쓰레드 상태가 존재합니다.
- New
- Runnable
- Waiting
- Timed Waiting
- Blocked
- Terminated
* 주의: 자바 쓰레드 상태가 OS 쓰레드 상태를 의미하는 것은 아닙니다.
1. New
쓰레드 객체는 생성되었지만 아직 start() 메서드가 호출되지 않은 상태로써 JVM에서는 객체가 존재하지만 아직 커널로의 실행은 안된 상태로 볼 수 있습니다.
2. Runnable
Runnable 상태는 'Ready To Run' 상태와 'Running' 상태로 세분화할 수 있습니다.
Ready To Run (실행 대기 상태)
- start() 메서드를 실행하면 내부적으로 커널로의 실행이 일어나고 커널 쓰레드로 1:1 매핑
- 쓰레드는 바로 실행 상태가 아닌 언제든지 실행할 준비가 되어 있는 실행 가능한 상태
- 쓰레드가 실행상태로 전환하기 위해서는 현재 쓰레드가 어떤 상태로 존재하든지 반드시 Ready To Run 상태를 거쳐야 함
Running (실행 상태)
- OS 스케줄러에 의해 스케줄링된 상태
- CPU를 할당받아 run() 메서드를 실행한 상태
Ready To Run 상태에서 Runnable 상태로 전환하기 위해 실행할 시간을 제공하는 역할은 OS 스케줄러가 수행합니다.
OS 스케줄러는 멀티 쓰레드 환경에서 각 쓰레드에게 고정된 시간을 할당해 Running 상태와 Ready To Run 상태를 오가도록 스케줄링하며 실행 상태에서 New와 Terminated 상태를 제외한 다른 상태로 전환될 때 쓰레드/프로세스 간 context switching이 일어납니다.
3. Waiting
쓰레드가 Running 상태에서 다른 쓰레드가 특정 작업을 수행하기를 기다리는 상태이며 wait() 메서드 혹은 join() 메서드에 의해 해당 상태로 전환됩니다.
- wait() 메서드는 다른 쓰레드에 의해 notify() 메서드를 받을 때까지 대기
- join() 메서드의 실행이 종료되거나 인터럽트가 발생할 때까지 대기
Waiting 상태의 쓰레드는 다른 쓰레드에 의해 interrupt 발생 혹은 notify(), notifyAll() 메서드가 호출될 경우 Ready To Run 상태로 전환됩니다.
4. Timed Waiting
쓰레드는 sleep() 메서드 및 timeout 매개변수가 있는 메서드를 호출할 때 시간이 지정된 대기 상태로 전환됩니다.
쓰레드의 대기 시간이 길어지고 CPU의 할당을 계속받지 못하는 상황 발생 시 starvation 상태가 발생되는데 Timed Waiting의 경우 대기 시간의 제한 시간이 존재하므로 해당 상황을 피할 수 있습니다.
해당 상태에서 지정된 시간이 지나거나 interrupt 발생 혹은 notify() 메서드가 호출되면 Ready To Run 상태로 전환됩니다.
5. Blocked
멀티 쓰레드 환경에서 두 개 이상의 쓰레드가 동기화된 critical section에 접근을 시도할 경우 Lock을 획득하지 못해 차단이 되고 Blocked 상태로 전환됩니다.
쓰레드는 Lock을 획득할 때까지 해당 상태에서 대기하며 Lock을 획득하면 Ready To Run 상태로 전환됩니다.
6. Terminated
run() 메서드 실행이 완료되거나 예외와 같이 비정상적으로 종료된 상태입니다.
앞서 언급했다시피 한번 종료된 쓰레드는 재실행이 불가합니다.
참고
자바 동시성 프로그래밍 [리액티브 프로그래밍 Part.1] - 정수원 강사님
https://mangkyu.tistory.com/258
'JAVA > 비동기 프로그래밍' 카테고리의 다른 글
[Java] 동기화 기법 (1) | 2024.03.04 |
---|---|
[Java] 동기화 개념 (0) | 2024.02.24 |
[Java] Java 쓰레드 활용 (1) | 2024.02.21 |
[Java] Java 쓰레드 기본 API (0) | 2024.02.05 |
[OS] 운영체제 기초 (0) | 2024.01.19 |