작성
·
309
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 인턴입니다.
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에서의 스레드 상태 관리 방법 등이 있습니다. 특히 멀티 스레딩 환경에서는 미묘한 타이밍 차이로 인해 실행 결과가 달라질 수 있으므로, 이를 충분히 고려해야 합니다.
이와 같은 멀티 스레딩 환경에서의 문제를 디버깅하거나 동작을 이해하는 것은 복잡할 수 있으므로, 여러 상황에서 실험하고 각각의 스레드 상태를 면밀히 관찰하는 것이 중요합니다.
추가적으로 강의와 관련된 내용은 해당 강의의 커뮤니티 질문&답변 게시판에 질문하시면 강사님께서 확인하시고 좀 더 심층적인 답변을 해주실 수 있습니다. 감사합니다.