Java Adv 04 - Thread 4
in Programming Language on Java
Join
- 앞서 Thread.sleep() 메서드를 통해 TIMED_WAITING 상태를 알아보았다. 이번에는 Join()메서드를 통해 WAITNG(대기 상태)가 무엇인지 왜 필요한지 알아보자
waiting(대기 상태)
- 스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태이다.
- TIMED_WAITING은 특정시간만 기다리는 상태이다.
package thread.control.join;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class JoinMainV0 {
public static void main(String[] args) {
log( "Start" );
Thread thread1 = new Thread( new Job(), "Thread- 1" );
Thread thread2 = new Thread( new Job(), "Thread- 2" );
thread1.start();
thread2.start();
log( "End" );
}
static class Job implements Runnable{
@Override
public void run() {
log( "작업 시작" );
sleep( 2000 ); //2초간 대기
log( "작업 완료" );
}
}
}
- 참고 : 스레드의 실행 순서는 보장되지 않는다. 실행 결과는 다를 수 있다.
Join이 필요한 상황
package thread.control.join;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class JoinMainV1 {
public static void main(String[] args) {
log( "Start" );
SumTask task1 = new SumTask( 1, 50 );
SumTask task2 = new SumTask( 51, 100 );
Thread thread1 = new Thread( task1, "thread1" );
Thread thread2 = new Thread( task2, "thread2" );
thread1.start();
thread2.start();
log( "task1.result = " + task1.result );
log( "task2.result = " + task2.result );
int sumAll = task1.result + task2.result;
log( "task1 + task2 = " + sumAll );
log( "End" );
}
static class SumTask implements Runnable {
int startValue; //0
int endValue; //0
int result = 0;
public SumTask(int startValue, int endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
@Override
public void run() {
log( "작업 시작" );
sleep( 2000 );
int sum = 0;
for (int i = startValue; i < endValue; i++) {
sum += i;
}
result = sum;
log( "작업 완료 + result = " + result );
}
}
}
- 0이 나온 이유는 main이 Thread1, Thread2에게 작업을 지시했지만 2초라는 sleep시간 동안 main이 이미 값을 조회하고 main을 종료시킨다. 그래서 계산값이 반환되기전에 결과값이 나와버린다.
- 여기서 핵심은 main 스레드가 thread1, thread2의 계산이 끝날 때 까지 기다려야 한다는 점이다. 그럼 어떻게 해야 main 스레드가 기다릴 수 있을까?
Join-join
package thread.control.join;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class JoinMainV3 {
public static void main(String[] args) throws InterruptedException {
log( "Start" );
SumTask task1 = new SumTask( 1, 50 );
SumTask task2 = new SumTask( 51, 100 );
Thread thread1 = new Thread( task1, "thread1" );
Thread thread2 = new Thread( task2, "thread2" );
thread1.start();
thread2.start();
//스레드가 종료될 때 까지 대기
//thread1, thread2의 작업이 종료되면 main thread의 작업이 종료된다.
log( "join() - main 스레드가 thread1, therad2 종료까지 대기" );
thread1.join();
thread2.join();
log( "task1.result = " + task1.result );
log( "task2.result = " + task2.result );
int sumAll = task1.result + task2.result;
log( "task1 + task2 = " + sumAll );
log( "End" );
}
static class SumTask implements Runnable {
int startValue; //0
int endValue; //0
int result = 0;
public SumTask(int startValue, int endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
@Override
public void run() {
log( "작업 시작" );
sleep( 2000 );
int sum = 0;
for (int i = startValue; i <= endValue; i++) {
sum += i;
}
result = sum;
log( "작업 완료 + result = " + result );
}
}
}
실행 결과를 보면 정확하게 5050 이 계산된 것을 확인할 수 있다.
예를 들어서
thread-1 이 아직 종료되지 않았다면 main 스레드는 thread1.join() 코드 안에서 더는 진행하지않고 멈추어 기다린다. 이후에 thread-1 이 종료되면 main 스레드는 RUNNABLE 상태가 되고 다음 코드로 이동한다. 이때 thread-2 이 아직 종료되지 않았다면 main 스레드는 thread2.join() 코드 안에서 진행하지 않고 멈추어 기다린다. 이후에 thread-2 이 종료되면 main 스레드는 RUNNABLE 상태가 되고 다음 코드로 이동한다. 이 경우 thread-1 이 종료되는 시점에 thread-2 도 거의 같이 종료되기 때문에 thread2.join() 은 대기하지 않고 바로 빠져나온다. 이렇듯 특정 스레드가 완료될 때 까지 기다려야 하는 상황이라면 join() 을 사용하면 된다.
Reference
인프런 김영한님의 자바강좌 + 나의 뇌