Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java에서 실행 중인 스레드를 안전하게 중단하는 방법 (interrupt() 완벽 정리)

Java에서 한 스레드를 중단(interrupt)하는 것은 해당 스레드가 직접 자신을 멈추는 것이 아니라, 다른 스레드가 대상 스레드의 Thread 객체에 대해 interrupt() 메서드를 호출함으로써 이루어집니다. 즉, 스레드 인터럽트는 외부에서 요청하는 '중단 신호'이며, 실제로 스레드를 강제 종료시키지 않고 협력적으로 작업을 취소할 수 있도록 설계된 메커니즘입니다.

Thread 클래스가 제공하는 3가지 인터럽트 메서드

  • void interrupt() — 대상 스레드에 인터럽트 신호를 보냅니다.
  • static boolean interrupted()현재 실행 중인 스레드가 인터럽트되었는지 검사합니다. 참고로 이 메서드는 호출 시 인터럽트 상태를 초기화(clear)한다는 특징이 있습니다.
  • boolean isInterrupted() — 해당 스레드 객체가 인터럽트되었는지만 검사하며, 인터럽트 상태를 변경하지 않습니다.

예제 코드

아래 예제는 메인 스레드가 작업 스레드를 시작한 직후 interrupt()를 호출하고, 작업 스레드 내부에서 Thread.interrupted()로 인터럽트 여부를 확인하여 루프를 종료하는 과정을 보여줍니다.

public class ThreadInterruptTest {
    public static void main(String[] args) {
        System.out.println("Thread main started");
        final Task task = new Task();
        final Thread thread = new Thread(task);
        thread.start();
        thread.interrupt(); // interrupt() 메서드 호출
        System.out.println("Main Thread finished");
    }
}
class Task implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("[" + Thread.currentThread().getName() + "] Message " + i);
            if(Thread.interrupted()) {
                System.out.println("This thread was interruped by someone calling this Thread.interrupt()");
                System.out.println("Cancelling task running in thread " + Thread.currentThread().getName());
                System.out.println("After Thread.interrupted() call, JVM reset the interrupted value to: " + Thread.interrupted());
                break;
            }
        }
    }
}

실행 결과

Thread main started
Main Thread finished
[Thread-0] Message 0
This thread was interruped by someone calling this Thread.interrupt()
Cancelling task running in thread Thread-0
After Thread.interrupted() call, JVM reset the interrupted value to: false

핵심 포인트 정리

1. interrupt()는 강제 종료가 아닙니다. 인터럽트는 단순히 스레드 내부의 '인터럽트 상태 플래그'를 true로 설정할 뿐입니다. 스레드가 이 신호를 무시하고 계속 실행할 수도 있습니다. 따라서 안전한 스레드 종료를 위해서는 run() 메서드 내부에서 주기적으로 인터럽트 상태를 확인하고, 적절히 작업을 정리한 뒤 종료하는 코드를 작성해야 합니다.

2. interrupted()와 isInterrupted()의 차이를 기억하세요. Thread.interrupted()는 현재 스레드의 인터럽트 상태를 반환한 후 false로 초기화하지만, thread.isInterrupted()는 상태를 그대로 유지하며 확인만 합니다. 위 출력 결과에서 두 번째 Thread.interrupted() 호출이 false를 반환한 것도 바로 이 때문입니다.

3. sleep(), wait(), join() 중이라면? 스레드가 대기 상태(sleep, wait, join 등)에 있을 때 인터럽트되면 InterruptedException이 발생하며, 이때 인터럽트 상태는 자동으로 초기화됩니다. 따라서 이 예외를 catch할 때는 일반적으로 다시 Thread.currentThread().interrupt()를 호출해 인터럽트 상태를 복원하거나, 작업을 중단하는 로직을 처리하는 것이 권장됩니다.