스레드 제어와 생명 주기1

Walter Mitty·2025년 4월 9일
0

개인공부

목록 보기
50/51

코드로 보는 스레드 생명 주기

import thread.start.HelloRunnable;
import static util.MyLogger.log;

public class ThreadInfoMain {

    public static void main(String[] args) {
        // main 스레드
        Thread mainThread = Thread.currentThread();
        log("mainThread = " + mainThread);
        log("mainThread.threadId()=" + mainThread.threadId());
        log("mainThread.getName()=" + mainThread.getName());
        log("mainThread.getPriority()=" + mainThread.getPriority());
        log("mainThread.getThreadGroup()=" + mainThread.getThreadGroup());
        log("mainThread.getState()=" + mainThread.getState());

        // myThread 스레드
        Thread myThread = new Thread(new HelloRunnable(), "myThread");
        log("myThread = " + myThread);
        log("myThread.threadId()=" + myThread.threadId());
        log("myThread.getName()=" + myThread.getName());
        log("myThread.getPriority()=" + myThread.getPriority());
        log("myThread.getThreadGroup()=" + myThread.getThreadGroup());
        log("myThread.getState()=" + myThread.getState());
    }
}

출력 👇

18:35:15.649 [     main] mainThread = Thread[#1,main,5,main]
18:35:15.652 [     main] mainThread.threadId()=1
18:35:15.652 [     main] mainThread.getName()=main
18:35:15.654 [     main] mainThread.getPriority()=5
18:35:15.654 [     main] mainThread.getThreadGroup()=java.lang.ThreadGroup[name=main,maxpri=10]
18:35:15.654 [     main] mainThread.getState()=RUNNABLE
18:35:15.654 [     main] myThread = Thread[#22,myThread,5,main]
18:35:15.654 [     main] myThread.threadId()=22
18:35:15.654 [     main] myThread.getName()=myThread
18:35:15.655 [     main] myThread.getPriority()=5
18:35:15.655 [     main] myThread.getThreadGroup()=java.lang.ThreadGroup[name=main,maxpri=10]
18:35:15.655 [     main] myThread.getState()=NEW

여기서 getState()을 보면 mainThread는 RUNNABLE,
myThread는 NEW라는걸 볼 수 있다.
NEW는 간단히 말해서 생성되었지만 동작을 안하는 상태라고 보면 되는데 좀 더 자세히 알아보자.

  • threadId() : 스레드의 고유 식별자를 반환하는 메서드. JVM 내에서 각 스레드에 대해 유일한 ID로 스레드가 생성될 때 할당되며 직접 지정할 수 없다.
  • getName() : 스레드의 이름을 반환하는 메서드. 생성자에서 myThread 라는 이름을 지정해줘서 이 값이 반환된 것이다.
    참고) 스레드 ID는 고유 식별자지만 이름은 중복될 수 있다.
  • getPriority() : 스레드의 우선순위를 반환한다. 1(가장 낮음)에서 10(가장 높음)까지 값으로 설정할 수 있고 기본값이 5다.
    참고) setPriority()를 통해 지정할 수 있다.
    • 어디까지나 이 우선순위를 희망해! 다. 우선순위는 스레드 스케줄러가 어떤 스레드를 우선 실행할지 결정하는 데 사용되지만 실제 실행 순서는 JVM 구현과 운영체제에 달라질 수 있다. 결정권은 나에게 없음 ^-ㅜ
  • getThreadGroup() : 스레드가 속한 스레드 그룹을 반환. 스레드 그룹은 스레드를 그룹화 하여 관리할 수 있는 기능을 제공하는데 기본적으로 모든 스레드는 부모 스레드와 동일한 스레드 그룹에 속하게 된다.
    • 스레드 그룹은 여러 스레드를 하나의 그룹으로 묶어서 특정 작업(예: 일괄 종료, 우선순위 설정 등)을 수행할 수 있다.
    • 부모 스레드(Parent Thread)란 새로운 스레드를 생성하는 스레드를 의미. 스레드는 기본적으로 다른 스레드에 의해 생성되는데(여지껏 main 스레드가 다른 스레드들을 생성했듯이) 이러한 생성관계에서 새로 생성된 스레드는 생성한 스레드를 부모로 간주한다.
      (그룹 기능은 직접적으로는 잘 사용되지 않으므로 그냥 참고!)
  • getState() : 스레드의 현재 상태를 반환하는 메서드. 반환되는 값은 Thread.State 열거형에 정의된 상수 중 하나다.

State 설명

  • 생성(NEW)이 되어 실행 가능한 상태로 실행되다가(RUNNABLE) 바로 종료(TERMINATED)가 될수도 있고
  • 아니면 차단(BLOCKED)되거나 대기(WAITING)하거나 시간 제한 대기(TIMED WAITING) 상태로 왔다 갔다 할 수 있다가 종료될 수도 있다.
    • 예) 스레드가 실행(러너블)되다가 sleep(시간 제한 대기)가 되었다가 다시 실행(러너블) 상태로 될 수 있다.

실제로 자바에서 스레드 일시 중지 상태(Suspended Stated)라는 상태는 없지만, 스레드가 기다리는 상태들을 묶어서 쉽게 이해하기 위해 사용할 예정!

  1. New
  • 스레드가 생성되고 아직 시작하지 않은 상태
  • 이 상태에서는 Thread 객체가 생성되지만, start() 메서드가 호출되지 않은 상태
    • 예)
      Thread thread = new Thread(runnable);

  1. Runnable
  • 스레드 실행될 준비 끝! 이 상태의 스레드는 실제로 CPU에서 실행될 수 있다.
  • start() 메서드가 호출되면 스레드는 Runnable 상태로 들어간다.
    • 예)
      thread.start();
  • 그렇다고 Runnable 상태의 모든 스레드가 동시에 실행되는 것은 아니고 스케줄러에 따라 대기열에 있다가 순서대로 CPU에서 실행된다.

참고) 궁금해서 Chat GPT 한테 물어봤다.
Q. 그럼 스케줄러 대기열에 들어가는 Thread는 모두 Runnable 상태여야 하는건가?
A. 운영체제 스케줄러에 의해 실행 후보가 되는 스레드는 기본적으로 “Runnable” 상태여야 한다. 따라서, 운영체제(OS) 입장에서는 Runnable 상태만이 “스케줄링 대상”이 된다.


  1. BLOCKED
  • 스레드가 다른 스레드에 의해 동기화 락을 얻기 위해 기다리는 상태
  • 예를들어 synchronized 블록에 진입하기 위해 락을 얻어야 하는 경우 이 상태에 들어간다.
    • 예) 이 코드 블럭에 진입하려고 할 때, 다른 스레드가 이미 lock을 가지고 있는 경우 해당 스레드는 BLOCKED 상태가 된다.
      synchronized (lock) {...}

  1. WAITING
  • 스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태
  • wait(), join() 메서드가 호출될 때 이 상태가 된다.
  • 스레드는 다른 스레드가 notify() or notifyAll() 메서드를 호출하거나, join() 이 완료될 때까지 기다린다.
    • 예)
      object.wait();

  1. TIMED_WAITING
  • 스레드가 특정 시간 동안 다른 스레드의 작업이 완료되기를 기다리는 상태
  • sleep(long millis), wait(long timeout), join(long millis) 메서드가 호출될 때 이 상태가 된다.
  • 주어진 시간이 경과하거나 다른 스레드가 해당 스레드를 깨우면 이 상태에서 벗어난다.
    • 예)
      Thread.sleep(1000);

  1. TERMINATED
  • 스레드 실행이 완료된 상태
  • 스레드가 정상적으로 종료되거나, 예외가 발생해 종료된 경우 이 상태로 들어간다
  • 스레드는 한 번 종료되면 다시 시작할 수 없다
  • 스레드의 run() 메서드가 완료되면 TERMINATED 상태가 된다.

참고) Thread.currentThread().getState()와 스레드 객체인 thread.getState()의 차이

  • Thread.currentThread().getState() 해당 코드를 실행하는 스레드 객체에 대한 state를 보여주고, 스레드 객체인 thread.getState()는 생성한 스레드 객체에 대한 상태를 볼 수 있다.
    차이를 분명하게 이해하길!
    예) 현재 main 스레드의 코드 구간에서 보고 있는데 상태를 찍어봤다면,
    Thread.currentThread().getState() > main 스레드의 상태
    스레드 객체인 thread.getState()는 main 스레드에서 만들어진 스레드 객체의 상태.

0개의 댓글