• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

interrupt() 강의 보다 정말 궁금한게 생겨서 문의 남깁니다.

24.03.04 19:42 작성 조회수 164

0

안녕하세요 강사님

강의를 듣다가 이상한 것을 발견하여 문의 드립니다.

 

Java Thread Fundamentals - 스레드 기본 API > interrupt() 강의중 InterruptExample 부분의 샘플 코드를 작성하고 테스트 및 정리를 하고 있습니다.

 

제가 현업에서 사용하는 JDK 11 을 사용해서 연습중에 이상한 현상을 발견하였습니다.

  • JDK 17에서는 문제 없음을 확인하였습니다.

     

먼저 코드 구현체 부분입니다.

public class TreadInterrupt {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("thread 1 start");
            System.out.println("thread 1 isInterrupted() = " + Thread.currentThread().isInterrupted());
        });
        Thread t2 = new Thread(() -> {
            System.out.println("thread 2 start");
            t1.interrupt();
            System.out.println("thread 1 isInterrupted() = " + t1.isInterrupted());
            System.out.println("thread 2 isInterrupted() = " + Thread.currentThread().isInterrupted());

        });

        t2.start();
        Thread.sleep(1000);
        t1.start();

        t1.join();
        t2.join();
        System.out.println("작업 완려");
    }

}

 

Thread#interrupt(), Thread#isInterrupted() 의 jdk 11과 17 버전입니다.

 

// jdk 11
public class Thread {

    public boolean isInterrupted() {
        return isInterrupted(false);
    }

   @HotSpotIntrinsicCandidate
    private native boolean isInterrupted(boolean ClearInterrupted);

   public void interrupt() {
        if (this != currentThread()) {
            this.checkAccess();
            synchronized(this.blockerLock) {
                Interruptible b = this.blocker;
                if (b != null) {
                    this.interrupt0();
                    b.interrupt(this);
                    return;
                }
            }
        }

        this.interrupt0();
    }

    private native void interrupt0();
    
}

 

// jdk 17
public class Thread {
    private volatile boolean interrupted;
    public boolean isInterrupted() {
        return interrupted;
    }

    public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupted = true;
                    interrupt0();  // inform VM of interrupt
                    b.interrupt(this);
                    return;
                }
            }
        }
        interrupted = true;
        // inform VM of interrupt
        interrupt0();
    }
}

위 코드와 같이 jdk 17 은 Thread 객체 내 interrupted 필드를 둠으로써 인터럽트의 상태를 관리하지만 jdk 11 같은 경우에는 native 메서드를 이용해서 인터럽트의 상태를 관리하는 것을 확인할 수 있었습니다.

 

이 때 jdk 11에서 위 코드를 실행시키면 결과가 아래 와 같이 출력되는것을 확인하였습니다.

thread 2 start
thread 1 isInterrupted() = false
thread 2 isInterrupted() = false
thread 1 start
thread 1 isInterrupted() = false
작업 완려

프로세스 실행을 반복하거나 sleep 의 시간을 늘려보아도 항상 아래와 같은 동일한 결과가 나왔습니다.

 

하지만 이 코드를 디버그 모드를 이용해서 동작시킨 경우 완전히 다른 결과가 나왔습니다.

  • break point 는 t1.interrupt() 부분에 걸었으며 실제 인터럽트 내부 코드 동작 부분은 확인하지 않고 F8 을 이용해서 코드라인을 프로세스가 종료할 때 까지 넘김

thread 2 start
thread 1 start
thread 1 isInterrupted() = true
thread 1 isInterrupted() = true
thread 2 isInterrupted() = false

위는 실제로 디버그 모드로 동작 시켰을때 처리 결과입니다.

 

아무리 생각해도 동일한 코드, 동일한 로직인데 디버그 모드일때 정상적으로 출력되고 일반적으로 실행하였을때 인터럽트의 상태가 모두 false 가 나온다는 것이 이해되지 않아 문의 남겨봅니다.

 

 

답변 1

답변을 작성해보세요.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2024.03.06

안녕하세요, 인프런 AI 인턴입니다.

gt님께서 문의하신 JDK 11과 JDK 17에서의 interrupt()isInterrupted()의 동작 차이에 대해 알아보겠습니다.

먼저, 문의 주신 코드에서 두 가지 중요한 점을 발견했습니다. 첫째, Thread.sleep(1000) 이후에 t1.start()가 호출되는데, 이는 t2가 먼저 실행되어 t1을 interrupt할 기회를 주고 있습니다. 둘째는 스레드 t2에서 t1.interrupt()가 호출되어 t1의 interrupt 상태를 변경하는 부분입니다.

사용된 JDK 버전 차이로 인해 동작 차이가 발생하는 주된 이유는 JDK 구현 내부에서 수행되는 최적화와 스케줄링 차이 때문일 수 있습니다. JDK 17에서는 interrupted 상태를 volatile 변수로 관리하지만, JDK 11에서는 native 메서드를 활용한 내부 상태 관리를 합니다. 이 차이로 인해 멀티 스레딩 환경에서의 동기화와 가시성 측면에서 성능 차이가 나타날 수 있습니다.

디버그 모드에서 다른 결과가 나타나는 부분은 디버거가 스레드의 실행을 제어하면서 실행의 타이밍과 컨텍스트 스위칭을 변경하기 때문입니다. 디버거가 중단점에서 스레드를 멈추고 사용자 입력을 기다리는 동안 다른 스레드는 계속 실행될 수 있으며, 이 점이 바로 일반 실행과 디버그 실행간 차이를 만드는 주요 원인입니다.

실행 결과에 영향을 미치는 요소로는 스레드 스케줄링, 스레드 시작(start())과 실행(run()) 사이의 타이밍, JVM에서의 스레드 상태 관리 방법 등이 있습니다. 특히 멀티 스레딩 환경에서는 미묘한 타이밍 차이로 인해 실행 결과가 달라질 수 있으므로, 이를 충분히 고려해야 합니다.

이와 같은 멀티 스레딩 환경에서의 문제를 디버깅하거나 동작을 이해하는 것은 복잡할 수 있으므로, 여러 상황에서 실험하고 각각의 스레드 상태를 면밀히 관찰하는 것이 중요합니다.

추가적으로 강의와 관련된 내용은 해당 강의의 커뮤니티 질문&답변 게시판에 질문하시면 강사님께서 확인하시고 좀 더 심층적인 답변을 해주실 수 있습니다. 감사합니다.