스레드의 상태

  • NEW: 스레드가 생성되고 아직 start()가 호출되지 않은 상태
  • RUNNABLE: 실행 중 또는 실행 가능한 상태
  • BLOCKED: 동기화 블록에 의해서 일시 정지된 상태
    • lock이 풀릴 때까지 기다리는 상태
  • WAITING, TIMED_WAITING: 스레드의 작업이 종료되지는 않았지만 실행가능하지 않은 (UNRUNNABLE) 일시정지 상태
    • TIMED_WAITING은 일시정지 시간이 지정된 경우임.
  • TERMINATED: 스레드의 작업이 종료된 상태

상태구하기

스레드객체.getState();

예제: T10

  • 스레드의 상태를 출력하는 예제

1단계: 모니터링 대상 스레드 생성

class TargetThread extends Thread {
  @Override
  public void run() {
    for (long i = 1; i <= 1000000000L; i++) {} // 시간 지연용
    try {
      Thread.sleep(1500); // TIMED_WAITING
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    for(long i=1; i<=1000000000L; i++) {} // 시간 지연용
  }
}

2단계: 스레드의 상태를 출력하기 위한 스레드 생성

class StatePrintThread extends Thread {
  private Thread targetThread; // 상태를 출력할 쓰레드가 저장될 변수

  public StatePrintThread(Thread targetThread) {
    this.targetThread = targetThread;
  }

  @Override
  public void run() {
    while (true) {
      // Thread의 상태 구하기 (getState()메서드 이용)
      Thread.State state = targetThread.getState();
      System.out.println("타켓 스레드의 상태값 : " + state);
      // sleep으로 자고 있을때: TIME-WAITING
      // for문돌아갈때: RUNNABLE
      // 다끝나면 TERMINATED...

      // NEW 상태인지 검사
      if (state == Thread.State.NEW) {
        targetThread.start(); // main에서 start시켜주는게 아니고 여기서 NEW체크후 start
      }

      // 타켓스레드가 종료 상태인지 검사
      if (state == Thread.State.TERMINATED) {
        break;
      }

      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

3단계: 프로그램 실행부 main()

public class T10_ThreadStateTest {
  public static void main(String[] args) {
    StatePrintThread spt = new StatePrintThread(new TargetThread());
    spt.start();
  } // main
} // class

상태를 변경하는 메서드

yield()

  • 현재 실행 대기중동등한 우선순위 이상의 다른 스레드에게 실행기회를 제공한다. (양보)
    • 내가 우선순위 5번이면, 1) 대기자가 5번이거나 2) 나보다 우선순위가 높은 애들 (ex:10번) 이면 양보가능, 그 밑이면 소용 없음.
  • 현재 실행중인 스레드의 상태를 Runnable 상태로 바꾼다.
    • Waiting이나 Blocked 상태로 바뀌지 않는다.
  • yield()메서드를 실행한다고 해서 현재 실행중인 스레드가 곧바로 Runnable 상태로 전이된다고 확신할 수 없다.

예시: T12

양보 기능 테스트용 쓰레드 클래스

class YieldThread1 extends Thread{
  @Override
  public void run() {
    for(int i = 1; i<=100; i++) {
      System.out.println("YieldTest1 : " + i);
      yield(); // 양보하기 
    }
  }
}

양보 기능이 없는 쓰레드 클래스

class YieldThread2 extends Thread{
  @Override
  public void run() {
    for(int i = 1; i<=100; i++) {
      System.out.println("YieldTest2 : " + i);
    }
  }
}

main()에서 실행

  Thread th1 = new YieldThread1();
  Thread th2 = new YieldThread2();

  th1.start();
  th2.start();

결과

  • 양보기능이 잘 작동하면 양보받은 YieldTest2부터 끝나지만 확신할 수 없다.

stop(), setStop(true), interrupt()

예시1: T13 (setStop, stop)

  • stop(): deprecated
    • Thread의 stop()메서드를 호출하면 쓰레드가 바로 멈춘다.
    • 이 때 사용하던 자원을 정리하지 못하고 프로그램이 종료되어서 나중에 실행되는 프로그램에 영향을 줄 수 있다.
    • 그래서 현재는 stop()메서드는 비추천(deprecated) 되어있다.

setStop, stop으로 멈춰볼 클래스

class ThreadStopEx1 extends Thread {
  private boolean stop; // 멤버변수 자동 초기화됨. 초기화값: false

  public void setStop(boolean stop) {
    this.stop = stop;
  }

  @Override
  public void run() {
    while(!stop) {
      System.out.println("쓰레드 처리중...");
    }
    System.out.println("자원 정리중...");
    System.out.println("실행 종료.");
  }
}

main()

ThreadStopEx1 th = new ThreadStopEx1();
th.start();

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  e.printStackTrace();
}

//th.stop(); // 강제종료. deprecated. 쓰지마!
th.setStop(true); // 상태 체크 후에 스레드 종료

stop() 으로 종료 시 결과

  • 자원 정리가 이루어지지 않음

setStop(true) 으로 종료시 결과

  • 자원 반납 후 종료됨

예시2: T13 (interrupt)

스레드가 실행될 main()

  • Runnable 상태에서는 interrupt 걸리지 않음, sleep 상태에만 interrupt 가능
// interrupt()메서드를 이용한 쓰레드 멈추기
ThreadStopEx2 th2 = new ThreadStopEx2();
th2.start();

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  e.printStackTrace();
}

th2.interrupt();// main이 th2를 interrupt한 것 
                // Runnable 상태에서는 interrupt 걸리지 않음, sleep 상태에만 interrupt 가능

interrupt() 메서드를 이용하여 멈출 스레드

방법1

  • sleep()메서드나 join()메서드 등을 사용했을 때 interrupt()메서드를 호출하면 InterruptedException이 발생한다.
  • 그동안 sleep, join 시 습관적으로 해줬던 예외처리가 훗날 interrupt()메서드를 쓸까봐 미리 방지하는 차원에서 했던 것임
class ThreadStopEx2 extends Thread {
  @Override
  public void run() {
    try{
      while(true){ // while문을 try안에 배치함으로써 예외발생시 catch후 종료
        System.out.println("쓰레드 처리 중...");
        Thread.sleep(1);
      }
    } catch (InterruptedException e) {
      e.printStackTrace(); // 주석처리하면 깔끔하게 종료됨!
    }
  }
  
  System.out.println("자원 정리 중...");
  System.out.println("실행 종료.");
}

방법1의 결과

  • e.printStackTrace(); 주석처리하면 깔끔하게 종료됨

방법2: interrupt()메서드가 호출되었는지 검사 (정석★)

방법2의 검사방법1: 인스턴스 메서드 isInterrupted()

class ThreadStopEx2 extends Thread {
  @Override
  public void run() {
    while(true){
    System.out.println("쓰레드 처리 중...");
    
    // 인스턴스 메서드 isInterrupted()
    if(this.isInterrupted()) { // interrupt()메서드가 호출되면 true
      System.out.println("인스턴스용 isInterrupted()");
      break;
    }
  }
  System.out.println("자원 정리 중...");
  System.out.println("실행 종료.");
}

결과

방법2의 검사방법2: 스태틱 메서드 interrupted()

  • 정적 메서드: 처음에 한번만 true, 나중에 interrupt 걸려도 false 리턴 -> 한번밖에 못씀
  • 여러번 쓰고 싶으면 인스턴스 메서드인 isInterrupted 사용
class ThreadStopEx2 extends Thread {
  @Override
  public void run() {
    while(true){
      System.out.println("쓰레드 처리 중...");

      // 정적 메서드 interrupted()
      if(Thread.interrupted()) { // interrupted() 메서드가 호출되면 true
        System.out.println("정적메서드 interrupted()");
        break;
      }
    }
    System.out.println("자원 정리 중...");
    System.out.println("실행 종료.");
  }
}

결과

profile
갈 길이 멀다

0개의 댓글