Java Adv 04 - Thread 4

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 );

        }
    }
}

image

  • 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 );

        }
    }
}

image 실행 결과를 보면 정확하게 5050 이 계산된 것을 확인할 수 있다.

image 예를 들어서 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

인프런 김영한님의 자바강좌 + 나의 뇌



© 2022. by taewoo

Powered by taewoo