join()은 Thread 클래스의 final 메소드로, 한 스레드의 실행 시작을 다른 스레드의 실행 종료에 연결하는 역할을 합니다. 즉, 특정 스레드가 완전히 종료되기 전까지는 다른 스레드가 실행을 시작하지 않도록 실행 순서를 제어할 수 있습니다. 스레드 인스턴스에 대해 join() 메소드를 호출하면, 현재 실행 중인 스레드는 해당 스레드 인스턴스가 작업을 마칠 때까지 대기(블로킹) 상태가 됩니다.
이 메소드는 InterruptedException을 던질 수 있으므로 호출 시 반드시 예외 처리(try-catch 블록 또는 throws 선언)를 해주어야 합니다. 참고로 join()은 오버로딩된 형태인 join(long millis)와 join(long millis, int nanos)도 함께 제공되며, 이를 활용하면 최대 대기 시간을 지정할 수 있습니다.
문법(Syntax)
public final void join() throws InterruptedException
예제 코드(Example)
아래 예제에서는 세 개의 스레드(t1, t2, t3)를 생성한 뒤, t1.start() 직후 t1.join()을 호출하여 t1이 모든 작업을 끝낸 후에 t2와 t3가 시작되도록 구성했습니다.
public class JoinTest extends Thread {
public void run() {
for(int i=1; i <= 3; i++) {
try {
Thread.sleep(1000);
} catch(Exception e) {
System.out.println(e);
}
System.out.println("TutorialsPoint "+ i);
}
}
public static void main(String args[]) {
JoinTest t1 = new JoinTest();
JoinTest t2 = new JoinTest();
JoinTest t3 = new JoinTest();
t1.start();
try {
t1.join(); // join() 메소드 호출
} catch(Exception e) {
System.out.println(e);
}
t2.start();
t3.start();
}
}실행 결과(Output)
TutorialsPoint 1 TutorialsPoint 2 TutorialsPoint 3 TutorialsPoint 1 TutorialsPoint 1 TutorialsPoint 2 TutorialsPoint 2 TutorialsPoint 3 TutorialsPoint 3
결과 분석
t1.join()이 호출되었기 때문에 메인 스레드는 t1이 1부터 3까지 모두 출력하고 종료할 때까지 기다립니다. 그 이후에야 t2와 t3가 거의 동시에 시작되므로, 두 스레드의 출력이 번갈아 나타나는 것을 확인할 수 있습니다. 만약 join()을 호출하지 않았다면 세 스레드가 동시에 실행되어 출력 순서가 실행할 때마다 달라졌을 것입니다. 이처럼 join()은 여러 스레드 간의 실행 순서를 보장해야 하는 상황에서 매우 유용하게 사용됩니다.