Java

Java 스레드와 OS 스레드의 관계 및 스레드 생명주기

2024.03.09

Java에서의 스레드 실행구조, 생명 주기 그리고 사용자 스레드와 데몬스레드를 이해해보고자 한다.

  • Java 스레드 실행과 실제 OS 스레드의 관계
  • Thread 클래스의 메서드 종류와 스레드 생명주기

1. Java 스레드 실행과 실제 OS 스레드의 관계

스레드는 os에 의해 관리되고 cpu core가 실행하는 최소 실행 단위이다. 즉, Java에서 Thread 클래스를 생성하고 실행하는 스레드는 결국 os에 의해 관리되어져야 하는 것을 의미한다. (정확히는 아래 나오는 사용자 수준 스레드와 커널 수준 스레드가 매핑이 되어야 한다.)

Java에서 생성한 스레드가 어떻게 os에 의해 관리되어 질까?

이 질문에 답을 하기 위해서는 우선 스레드의 유형에 대해 알아야한다.스레드의 유형에는 사용자 수준 스레드(User Level Threads)커널 수준 스레드(Kernel Levels Threads) 가 있다.

  • 사용자 수준 스레드(User Level Threads)

    • 운영체제 커널이 아닌 사용자 영역에서 관리되는 스레드로, 커널의 도움없이 사용자 수준의 라이브러리나 프레임워크에서 생성하고 관리된다.
    • 스케줄링을 위한 커널호출 작업이 없기 때문에 그에 대한 오버헤드가 적다.
  • 커널 수준 스레드(Kernel Level Threads)

    • 운영체제 커널이 직접 관리하는 스레드로, os 스케줄러에 의해 스케줄링된다.
    • 스케줄링을 위해 사용자모드에서 커널모드로 전환하는 오버헤드가 발생한다.(System call)

Java의 스레드 모델은 JVM이 유저 스레드를 생성하면, Java Native Interface(JNI)를 통해 커널 스레드와 1:1로 매핑하는 형태이다. 덕분에 각 유저 스레드를 커널에서 관리할 수 있게된다. Thread 클래스 내부의 native method인 start0()를 사용함으로써 시스템 콜 호출을 통한 커널 스레드 생성을 요청한다.

jvm architecture



Java에서 생성하는 유저 스레드와 커널 스레드의 1:1 매핑을 개략적으로 구조화 하면 아래와 같다. jvm architecture


2. Thread 클래스의 메서드 종류와 스레드 생명주기

Java에서 스레드는 어떠한 상태를 가지면서 생명주기가 관리되는지, 어떤 메서드를 통해서 상태들이 어떻게 변화하는지를 알아보자.

2.1 Thread의 상태

jvm architecture

Thread 클래스 내부에는 스레드 상태를 정의하는 State라는 ENUM이 존재한다. 자바 스레드는 항상 6가지 상태 중 하나의 상태만을 가질 수 있는데, 이 상태는 os 커널의 스레드 상태를 의미하지는 않는다. Thread.State를 통해 ENUM 값에 접근할 수 있으며, State 값들은 아래와 같이 있다.

1public enum State { 2 3 /** 4 * 스레드가 아직 시작되지 않은 상태. 5 * 스레드가 생성되었지만 아직 시작 메서드(start())가 호출되지 않은 상태이다. 6 */ 7 NEW, 8 9 /** 10 * 스레드가 실행 중이거나 실행 가능한 상태. 11 * 스레드가 cpu를 할당받아 실행 중이거나, cpu를 할당받기 위해 대기중인 상태이다. 12 */ 13 RUNNABLE, 14 15 /** 16 * 스레드가 모니터 락을 기다리며 차단된 상태. 17 * 동기화된 블록/메서드에 진입하기 위해 모니터 락이 해제될 때까지 기다리는 상태이다. 18 */ 19 BLOCKED, 20 21 /** 22 * 스레드가 대기 중인 상태. 23 * 스레드가 다른 특정 조건이 충족될 때까지 기다리고 있는 상태이다. 24 * 예를 들어, wait()를 호출한 스레드는 다른 스레드에 대해 notify() 혹은 notifyAll() 호출을 기다린다. 25 */ 26 WAITING, 27 28 /** 29 * 스레드가 대기 중인 상태. 30 * 대기 시간이 지정된 상태이다. 31 * 예를 들어, sleep()을 통해 지정된 대기 시간만큼 대기 상태에 있는 스레드이다. 32 */ 33 TIMED_WAITING, 34 35 /** 36 * 스레드가 종료된 상태. 37 * 스레드가 실행을 완료. 38 */ 39 TERMINATED; 40}

각 스레드가 6가지 상태 중 하나를 가지면서 NEW ~ TERMINATED 상태까지 여러 상태를 거치면서 하나의 Life Cycle을 가진다.

2.2 Java Thread의 생명주기(Life Cycle)

자바에서 스레드가 어떤 메서드를 통해서 상태를 바꿔가면서 생명주기가 관리되는지 알아보고자 한다.

java thread life cycle

2.1에서 살펴본 상태들은 위 그림대로 생명주기를 가진다. 크게 정리하면 총 다섯 가지 상황이 존재한다.

  1. 스레드 생성
  2. 스레드 실행 가능한 상태 (Runnable)
  3. 스레드 실행 상태 (Runnable)
  4. 스레드 대기 상태 (Waiting, Blocked, Timed Waiting)
  5. 스레드 종료 상태 (Terminated)

특히, Runnable 상태 중에서도 실행상태에서 대기상태로 가는 상황대기상태에서 실행대기상태로 가는 상황들을 잘 이해해야 한다. 다섯가지 상황을 살펴보며 자바에서는 어떤 메서드를 사용해서 상태를 변경하는지 살펴보자.


2.2.1 스레드 생성

1Thread thread = new Thread();

스레드 객체 생성 상태이다. 스레드 객체가 생성되었지만 아직 start()를 통해 실행되지 않은 상태이다. 즉, 커널 스레드는 아직 생성되지 않고 자바 스레드 객체만 덩그러니 생성된 상태이다.



2.2.2 스레드 실행대기

1thread.start()

start() 호출이 일어나면 본격적으로 커널 스레드와 1:1 매핑이 이루어진다. 이때, 실행 가능한 상태가 된다. 즉, os 스레드에서의 Ready 상태라고 볼 수 있으며 CPU가 스레드를 선점하게 되면 그때서야 실행된다.


2.2.3 스레드 실행대기 -> 실행

1// run() 2Thread thread = new Thread( 3 new Runnable() { 4 @Override 5 public void run() { 6 System.out.println("Runnable"); 7 } 8 } 9); 10 11// or 12 13Thread thread = new Thread(() -> System.out.println("Runnable"));

스레드가 CPU를 선점하면 정의한 run()메서드를 실행한다. 자바에서는 Runnable 인터페이스의 run() 메서드를 구현하여, 해당 스레드의 실행코드를 작성할 수 있다. 실행상태에서는 타임아웃, Lock 등에 의해 컨텍스트 스위칭이 발생하면서 대기 상태로 전환될 수 있다.


2.2.4 스레드 실행 -> 실행대기

현재 실행 중인 스레드는 cpu scheduler에 의해 대기상태로 전환(suspended) 되거나, 자바에서 Thread.yield() 메서드를 통해 다른 스레드에게 제어권을 양보하며 실행대기 상태로 전환될 수 있다.


2.2.5 스레드 실행 -> Timed Waiting

1thread.sleep(int millis); 2thread.wait(int millis); 3thread.join(int millis);

java에서는 지정된 시간만큼 스레드를 대기 시킬 수 있다. 시간을 지정하여 대기상태로 전환되는 경우 TIMED_WAITING 상태라고 한다. sleep(), wait(), join() 등 메서드에는 스레드 대기시간을 지정할 수 있다. 대기시간을 명시함으로써 기아상태를 피할 수도 있다.


2.2.6 Timed Waiting -> 스레드 실행대기

대기시간 지정을 통해 TIMED_WAITING 상태에 있던 스레드는 지정한 시간이 끝나거나, 인터럽트, notify() 등을 통해 다시 실행대기 상태로 전환될 수 있다.


2.2.7 스레드 실행 -> BLOCKED

스레드 실행 도중 critical section(임계 영역)에 접근하면 BLOCKED 상태로 바뀔 수도 있는데, 이는 멀티 스레드 환경에서 이미 다른 스레드에 의해 임계영역의 락이 선점되어 있으면 락 대기에 의해 BLOCKED 상태가 된다.

그리고, 락을 획득하게 되면 다시 스레드 실행 대기상태가 된다.


2.2.8 스레드 실행 -> WAITING

1thread.wait(); 2thread.join();

스레드가 실행 중이다가, join()이나 wait() 등의 메서드와 같이 다른 스레드의 작업이 완료되기 까지를 기다리는 상태이다. join()은 다른 스레드의 실행이 종료될 때까지 대기한다. 그리고, wait()은 호출한 스레드 자체를 대기상태로 바꾸고, 다른 스레드에 의해 notify()를 받을 때까지 대기하는 동기화 기법이기도 하다.

물론, wait()과 join()이 각각 notify()를 받거나 다른 스레드의 실행이 종료되면 다시 스레드 실행대기 상태로 전환된다.


2.2.9 스레드 실행 -> TERMINATED

스레드의 run()메서드 실행이 완료되었거나 예외가 발생한 경우 스레드는 종료된다.



[참고 자료]

https://download.java.net/java/early_access/valhalla/docs/api/java.base/java/lang/Thread.html https://download.java.net/java/early_access/valhalla/docs/api/java.base/java/lang/Thread.State.html



© 2024. seongju all rights reserved.