우선, join()을 설명하기전 사용유무에 대한 차이점을 알아보기 위해서, 사용하지 않은 코드부터 보자.
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, "thread-1");
Thread thread2 = new Thread(task2, "thread-2");
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;
int endValue;
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);
}
}
}
위의 코드를 보면 , thread1,thread2를 start() 하고 , join() 을 사용하지 않았다. 출력결과는 어떻게 될까 ? 과연 의도한대로 sumAll변수에 각각의 쓰레드에서 더한값의 결과가 담기게될까 ?
출력결과
15:36:28.347 [ main] Start
15:36:28.349 [ thread-1] 작업 시작
15:36:28.349 [ thread-2] 작업 시작
15:36:28.352 [ main] task1.result = 0
15:36:28.352 [ main] task2.result = 0
15:36:28.352 [ main] task1 + task2 = 0
15:36:28.352 [ main] End
15:36:30.355 [ thread-1] 작업 완료 result=1275
15:36:30.355 [ thread-2] 작업 완료 result=3775
출력결과를 보면 , 기대와는 다르게 메인쓰레드에서 호출한 thread1,thread2 모두 0으로 나오고 , 각각의 쓰레드에서만 결과가 제대로 나왔다. 위의 결과가 나온 상황을 아래의 그림을 통해 보겠다.

main 스레드는 thread-1 , thread2 에 작업을 지시하고, thread-1 , thread2 가 계산을 완료하기도 전에 먼저 계산 결과를 조회했다. 참고로 thread-1 , thread-2 가 계산을 완료하는데는 2초 정도의 시간이 걸린다(코드를 잘보면 sleep 2초를 설정했다). 따라서 결과가 task1 + task2 = 0 으로 출력된다.

main 스레드는 두 스레드를 시작한 다음에 바로 task1.result , task2.result 를 통해 인스턴스에 있는 결과 값을 조회한다. 참고로 main 스레드가 실행한 start() 메서드는 스레드의 실행이 끝날 때 까지 기다리지 않는다! 다른 스레드를 실행만 해두고, 자신의 다음 코드를 실행할 뿐이다!
의문이 들어야하는점
의문점
여기서 문제의 핵심은 main 스레드가 thread-1 , thread-2 의 계산이 끝날 때 까지 기다려야 한다는 점이다. 그럼 어떻게 해야 main 스레드가 기다릴 수 있을까?
아래의 코드는 join()을 사용한 코드이다.
public class JoinMainV2 {
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, "thread-1");
Thread thread2 = new Thread(task2, "thread-2");
thread1.start();
thread2.start();
// 스레드가 종료될 때 까지 대기
log("join() - main 스레드가 thread1, thread2 종료까지 대기");
thread1.join();
thread2.join();
log("main 스레드 대기 완료");
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;
int endValue;
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);
}
}
}
앞서 join() 을 사용하기전에는 sumAll 변수에 원하는 값이 나오지않았다. join() 을 사용한 코드는 과연 sumAll에 원하는 값이 담기게될까 ?
실행결과🔽
16:46:54.788 [ main] Start
16:46:54.790 [ thread-1] 작업 시작
16:46:54.790 [ thread-2] 작업 시작
16:46:54.790 [ main] join() - main 스레드가 thread1, thread2 종료까지 대기
16:46:56.801 [ thread-2] 작업 완료 result = 3775
16:46:56.801 [ thread-1] 작업 완료 result = 1275
16:46:56.802 [ main] main 스레드 대기 완료
16:46:56.803 [ main] task1.result = 1275
16:46:56.803 [ main] task2.result = 3775
16:46:56.804 [ main] task1 + task2 = 5050
16:46:56.804 [ main] End
실행 결과를 보면 정확하게 5050 이 계산된 것을 확인할 수 있다.

main 스레드에서 다음 코드를 실행하게 되면 main 스레드는 thread-1 , thread-2 가 종료될 때 까지 기다린다. 이때 main 스레드는 WAITING 상태가 된다.
thread1.join(); <- main 쓰레드 WAITING 상태
thread2.join(); <- main 쓰레드 WAITING 상태
예를 들어서 thread-1 이 아직 종료되지 않았다면 main 스레드는 thread1.join() 코드 안에서 더는 진행하지않고 멈추어 기다린다. 이후에 thread-1 이 종료되면 main 스레드는 RUNNABLE 상태가 되고 다음 코드로 이동한다.
이때 thread-2 이 아직 종료되지 않았다면 main 스레드는 thread2.join() 코드 안에서 진행하지 않고 멈추어 기다린다. 이후에 thread-2 이 종료되면 main 스레드는 RUNNABLE 상태가 되고 다음 코드로 이동한다.
join의 단점
하지만 join() 의 단점은 다른 스레드가 완료될 때 까지 무기한 기다리는 단점이 있다. 비유를 하자면 맛집에 한 번 줄을 서면 중간에 포기하지 못하고 자리가 날 때 까지 무기한 기다려야 한다. 만약 다른 스레드의 작업을 일정 시간 동안만
기다리고 싶다면 어떻게 해야할까?
join() 은 두 가지 메서드가 있다.