본문 바로가기

프로그래밍언어/JAVA

[JAVA] 쓰레드 실행제어

반응형

1. 쓰레드 우선순위

 

쓰레드는 우선순위라는 속성을 가지고 있다. 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다. 각 쓰레드가 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 지정하여 실행시간을 조절할 수 있다.

 

- 쓰레드의 우선순위 지정

 

void setPriority(int newPriority) // 쓰레드의 우선순위를 지정한 값으로 변경한다.
int getPriority() // 쓰레드의 우선순위를 반환한다.

public static final int MAX_PRIORITY = 10; // 최대우선순위
public static final int MIN_PRIORITY = 1; // 최소우선순위
public static final int NORM_PRIORITY = 5; // 보통우선순위

 

쓰레드의 우선순위와 관련된 메서드와 상수는 위와 같다. 우선순위는 1 ~ 10 의 값을 가질 수 있고, 숫자가 클수록 우선순위가 높다. 쓰레드의 우선순위는 해당 쓰레드를 생성한 부모 쓰레드에서 상속을 받는다. main 쓰레드에서 수행하는 쓰레드는 우선순위가 5이므로 main 메서드에서 생성하는 쓰레드의 우선순위는 기본값 5를 가진다.

 

 

쓰레드의 실행제어

- 쓰레드의 상태

 

쓰레드는 다음의 상태를 가진다.

 

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

 

위에 기술한 쓰레드 상태를 실제 쓰레드가 라이프사이클에 맞춰 설명하면 다음과 같다.

 

1) 쓰레드를 생성하고 start() 를 호출하면 생성된 쓰레드는 실행대기열에 저장되어 자신의 순서를 기다린다.
실행대기열은 큐 구조로 되어 있어서 먼저 들어온 쓰레드가 먼저 실행된다.

2) 실행대기상태에 있던 쓰레드는 자신의 차례가 되면 실행상태가 된다.

3) 주어진 실행 시간이 다되거나 yield() 를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.

4) 실행 중에 suspend(), sleep(), wait(), join(), I/O block 등에 의해 일시정지상태가 될 수 있다. I/O block 과 같은 경우에는 사용자의 입력을 기다리다가 입력이 완료되면 다시 실행대기상태가 된다.

5) 지정된 일시정지시간이 다되거나 (time-out), notify(), resume(), interrupt() 가 호출되면 일시정지상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다린다.

6) 실행을 모두 마치거나 stop() 이 호출되면 쓰레드는 소멸된다.

 

쓰레드와 관련된 메서드들은 다음과 같다.

- sleep(long milis)

일정시간동안 쓰레드를 멈추게 한다. 밀리세컨드, 나노세컨드 단위로 시간을 설정할 수 있다.
sleep() 에 의해서 일시정지 상태가 된 쓰레드는 지정된 시간이 다 되거나 interrupt() 가 호출되면, InterruptedException 이 발생되어 잠에서 깨어나 실행대기 상태가 된다. 그래서 sleep() 을 호출할 때는 항상 try-catch 문으로 예외를 처리해줘야 한다.

sleep() 은 항상 현재 실행 중인 쓰레드에 대해 작동하기 때문에 특정 쓰레드 인스턴스를 통해 sleep() 을 호출하여도 실제로 영향을 받는 것은 해당 메서드를 실행하는 쓰레드이다. 그렇기 때문에 sleep() 은 static 으로 선언되어 있으며, 참조변수를 사용하기보다 Thread.sleep() 과 같이 클래스 메서드로 호출하여 사용해야 한다.

- interrupt(), interrupted()

쓰레드의 작업을 취소시킨다.
interrupt() 는 쓰레드에게 작업을 멈추라고 요청한다. 작업 종료를 요청할 뿐 쓰레드를 강제로 종료시키지는 못한다. 그저 쓰레드의 interrupted 상태를 바꾼다.

interrupted() 는 쓰레드에 대해 interrupt() 가 호출되었는지 여부를 반환하다. isInterrupted() 도 쓰레드의 interrupt() 가 호출되었는지 확인하는데 사용할 수 있는데, interrupted() 와 달리 쓰레드의 interrupted 상태를 초기화하지 않는다.

쓰레드가 sleep(), wait(), join() 에 의해 WAITING 상태에 있을 때, 해당 쓰레드에 대해 interrupt() 를 호출하면, InterruptedException 이 발생하고 쓰레드는 RUNNABLE 로 바뀐다. 쓰레드를 꺠워서 실행가능한 상태로 만드는 것이다.

- suspend(), resume(), stop()

suspend() 는 sleep() 처럼 쓰레드를 멈추게 한다. suspend() 로 정지된 쓰레드는 resume() 을 호출해야 다시 실행대기 상태가 된다. stop() 은 호출되는 즉시 쓰레드가 종료된다.

suspend(), resume(), stop() 은 쓰레드의 실행을 제어하는 가장 쉬운 방법이지만 데드락을 일으키기 쉽게 작성되어 있어서 사용이 권장되지 않는다. 이때문에 Java API 에서는 deprecated 되었다.

- yield

yield() 는 호출한 쓰레드의 실행 시간을 다음 차례의 쓰레드에게 양보하도록 한다. 스케줄러에게 할당받은 시간이 남아있어도 yield() 가 호출되면 작업을 중단하고 실행대기상태가 된다.

- join

쓰레드 자신이 하던 작업을 잠시 멈추고, 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 할 때 사용한다.

 

join()
join(long millis)
join(long millis, int nanos)

 

위와 같이 매개변수 시간을 지정해줄 수 있다. 매개변수로 시간을 지정해주지 않으면, 해당 쓰레드가 작업을 모두 마칠 때까지 기다리게 된다. 작업 도중에 다른 쓰레드의 작업이 먼저 수행되어야할 필요가 있을 때 join() 을 사용한다.

반응형