Java Thread - (2) 제어
Thread 제어
Thread 일종의 생명주기 (LifeCycle)을 가집니다.
보통은 start()메소드를 호출해 쓰레드가 작업을 시작하게 합니다.
이번 포스팅에서는 몇가지 Thread를 제어하는 메소드들을 알아보고자 합니다.
- Thread.sleep() - static메소드로 현재 이 메소드를 호출하는 Thread가 일정 시간동안 멈춰있게 합니다.
- wait() / join() - 2개 혹은 그 이상의 Thread들을 기다리거나 협업할 수 있게 합니다.
- interrupt() - sleep중이거나 wait중인 Thread들을 재가동 시킵니다.
위의 메소드들에 대한 예제를 구현해보겠습니다.
sleep
sleep중인 Thread는 아무것도 하지 않고 기다리는 상태 (IDLE상태)입니다.
이 작업은 일정 시간동안 아무 입력도 받지 않을 수 있고, 작업을 수행하지 않습니다.
다른 Thread들과 경쟁하지도 않고, 거의 CPU를 쓰지 않는 상태입니다.
아래는 CounterThread에 sleep을 적용한 예제입니다.
public class CounterRunnableSleep implements Runnable {
private int counter = 0;
private boolean running = true;
Thread thread;
public CounterRunnableSleep(){
thread = new Thread(this);
thread.run();
}
@Override
public void run() {
while (running){
counter++;
System.out.println(counter);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (counter >= 10) running = false;
}
}
public static void main(String[] args) {
Runnable thread = new CounterRunnableSleep();
}
}
결과를 보시면 아래와 같이 1초에 한번씩 총 10번 숫자를 출력합니다.
> Task :CounterRunnableSleep.main()
1
2
3
4
5
6
7
8
9
10
join
Join은 조금 하드한 방법의 동기화 기법입니다.
소프트하게 동기화를 하는 방법은 synchronized라는 방법이 있는데, 나중에 살펴보도록 하겠습니다.
join메소드는 한 Thread가 다른 Thread가 한 시점에서 만날 수 있도록 해주는 메소드 입니다.
join메소드는 일정시간 기다리다가 포기하게 하도록 시간을 지정해 줄 수도 있고,
무작정 기다리게도 할 수 있습니다.
다음은 join을 사용한 예제입니다.
public class CounterThreadJoin extends Thread {
private int counter = 0;
private boolean running = true;
@Override
public void run() {
while (running){
counter++;
System.out.println(counter);
if (counter >= 10) running = false;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
String mainThreadName = Thread.currentThread().getName();
CounterThreadJoin thread = new CounterThreadJoin();
thread.start();
System.out.println(String.format("%s, started", mainThreadName));
thread.join();
System.out.println(String.format("%s, terminated", mainThreadName));
}
}
위의 코드의 main메소드를 보면, main Thread가 CounterThread를 join을 통해 기다리게 됩니다.
main Thread는 원래 모든 Thread가 종료될때까지 기다리지는 않습니다.
하지만 위 예제에서는 join을 호출해주었기 때문에 main Thread는 바로 종료하지 않고 기다렸다가 종료하게 됩니다.
위 코드의 실행 결과는 다음과 같습니다.
> Task :CounterThreadJoin.main()
1
main, started
2
3
4
5
6
7
8
9
10
main, terminated
interrupt
interrupt메소드는 Thread가 sleep중이거나 wait중일 때 이 상태를 캔슬 시키고 재구동하게하는 방법입니다.
이번에는 두개의 Thread를 따로 구현해서 실험을 해보도록 하겠습니다.
먼저 CounterThreadSleep을 구현해줍니다. 이 Thread는 10초에 한번씩 숫자를 print하게 되어있습니다.
public class CounterThreadSleep extends Thread {
private int counter = 0;
private boolean running = true;
@Override
public void run() {
while (running){
counter++;
System.out.println(counter);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("Thread has been interrupted!");
}
if (counter >= 10) running = false;
}
}
}
그리고 InterrupterThread를 구현해주겠습니다.
이 Thread는 3초간 sleep하다가 생성자에서 받아준 Thread를 interrupt시킵니다.
public class InterrupterThread extends Thread {
CounterThreadSleep counterThread;
public InterrupterThread(CounterThreadSleep counterThread){
this.counterThread = counterThread;
}
@Override
public void run() {
System.out.println("Interrupter started");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counterThread.interrupt();
System.out.println("Interrupter ended");
}
public static void main(String[] args) throws InterruptedException {
CounterThreadSleep counterThread = new CounterThreadSleep();
InterrupterThread interrupterThread = new InterrupterThread(counterThread);
counterThread.start();
interrupterThread.start();
}
}
하단의 main함수에서는 두개의 Thread를 모두 초기화 시키고 start를 해주고 있습니다.
이 코드의 실행 결과는 다음과 같습니다.
> Task :InterrupterThread.main()
1
Interrupter started
Interrupter ended
Thread has been interrupted!
2
3
4
5
6
7
8
9
10
실행을 해보시면, 원래대로라면 실행을 하고 10초간 기다려야 하지만,
interrupt가 되기 때문에 Interrupter가 ended되고, CounterThread가 Interrupt되면서, 바로 2가 나오는 것을 보실 수 있습니다.
참고자료: Learning Java, 4th Edition, Orielly