티스토리 뷰
목차
자바 쓰레드 인터럽트 블로킹 해결 (Java Thread Interrupt Blocking)
자바 - 인터럽트에 응답하지 않는 블로킹 작업 다루기
자바Java 라이브러리에 포함된 여러 블로킹 메소드는 대부분 Thread 인터럽트가 발생하는 즉시 멈추면서 InterruptedException을 띄우게 되어 있다. 따라서 작업 중단 요청에 적절하게 대응하는 작업을 쉽게 구현할 수 있다.
그런데 잘 살펴보면 모든 Java 블로킹 메소드가 인터럽트에 대응하게 되어 있지는 않다.
예를 들어 동기적인 소켓 I/O를 실행하는 도중에 스레드가 멈춰 있는 경우라던가 암묵적인intrinsic 락을 확보하기 위해 대기하는 등의 작업에 멈춰있는 경우라면, Interrupt를 거는 것이 인터럽트 상태 변수의 값을 설정하는 것 말고는 아무런 실제적 효과가 없다.
일부 상황에서는 인터럽트와 유사한 기법을 활용해 인터럽트에 반응하지 않는 블로킹 메소드에서 대기 중인 스레드가 작업을 멈추도록 할 수 있긴 하지만, 이런 작업을 하고자 할 때는 해당 스레드Thread가 대기 상태에 멈춰 있는 이유가 무엇인지를 훨씬 정확하게 이해해야 한다.
자바 인터럽트 문제 - java.io 패키지의 동기적 소켓 I/O
서버 애플리케이션에서 가장 대표적인 블로킹 I/O의 예는 바로 소켓에서 데이터를 읽어오거나 데이터를 쓰는 부분이다. InputStream 클래스의 read 메소드와 OutputStream의 write 메소드가 Interrupt에 반응하지 않게 되어 있다는 단점이 있지만, 해당 스트림이 연결된 소켓을 직접 닫으면 대기 중이던 read나 write 메소드가 중단되면서 SocketException이 발생한다.
자바 인터럽트 문제 - java.nio 패키지의 동기적 I/O
InterruptibleChannel에서 대기하고 있는 스레드에 인터럽트를 걸면 CLosedByInterruptException이 발생하면서 해당 채널이 닫힌다(더불어 해당 채널에서 대기하고 있던 모든 스레드Thread에서 ClosedByInterruptException이 발생한다).
InterruptibleChannel을 닫으면 해당 채널로 작업을 실행하던 스레드에서 AsynchronousCloseException이 발생한다. 블로킹 대부분의 표준 Channel은 모두 InterruptibleChannel을 구현한다.
자바 인터럽트 문제 - Selector를 사용한 비동기적 I/O
스레드가 Selector 클래스(jajva.nio.channels 패키지)의 select 메소드에서 대기 중인 경우, close 메소드를 호출하면 CLosedSelectorException을 발생시키면서 즉시 리턴된다.
자바 인터럽트 문제 - 락 확보
스레드가 암묵적인 락을 확보하기 위해 대기 상태에 들어가 있는 경우 언젠가 락을 확보할 수 있을 것이라는 보장을 하지 못할뿐더러 어떤 방법으로든 다음 상태로 진행해 Java 스레드의 주의를 끌 수 없으므로 어떻게 해 볼 방법이 없다.
하지만 Lock 인터페이스를 구현한 락 클래스의 lockInterruptibly 메소드를 사용하면 락을 확보할 때까지 대기하면서 인터럽트에도 응답하도록 구현할 수 있다.
ReaderThread는 소켓 하나를 연결해서 사용하는데 소켓으로 들어오는 내용을 동기적으로 읽어 들이고, 읽은 내용을 모두 processBuffer 메소드에 넘긴다.
ReaderThread 클래스는 사용자가 접속해 연결된 소켓을 닫아버리거나 프로그램을 종료시킬 수 있도록 interrupt 메소드를 오버라이드해 인터럽트를 요청하는 표준적인 방법과 함께 추가로 열려있는 소켓을 닫는다.
이렇게 하면 ReaderThread 클래스에 Interrupt를 걸었을 때 read 메소드에서 대기 중인 상태이거나 기타 인터럽트에 응답할 수 있는 블로킹 메소드에 멈춰 있을 때도 작업을 중단시킬 수 있다.
자바 쓰레드 인터럽트 블로킹 해결 (Java Thread Interrupt Blocking)