반응형
7장 중단 및 종료
- 작업을 취소하고 인터럽트를 거는 부분에 대한 개념, 작업이나 서비스가 취소 요청에도 잘 반응하도록 프로그램하는 방법에 대해 알아본다.
- 7.1 작업 중단
- 7.2 스레드 기반 서비스 중단
- 7.3 비정상적인 스레드 종료 상황 처리
- 7.4 JVM 종료
7.1 작업 중단
- 자바에는 스레드가 작업을 실행하고 있을 때 강제로 멈추도록 하는 방법이 없음
- 대신 인터럽트(interrupt)를 사용할 수 있음. 인터럽트란, 특정 스레드에게 작업을 멈춰달라고 요청하는 것
(명령이 아닌 요청이다.)
- 실행 중인
스레드를 강제로 멈추게 하기 보다는 작업을 진행하던 스레드에서 스스로 마무리하는 것이 정상적으로 종료할 수 있어서 가장 적절한 방법임
7.1 작업 중단
- 실행 중인 작업을 취소하고자 하는 요구사항이 생기는 경우들
1. 사용자가 취소하기를 요청한 경우
2. 시간이 제한된 작업
3. 애플리케이션 이벤트
4. 오류
5. 종료
7.1 작업 중단
- 작업을 실행하는 스레드가 스스로 멈추게 하는 방법 중 가장 기본적인 방법은 취소 상태에 대한 플래그값을 두고 작업에서
주기적으로 확인하는 방법
=> 별로 좋지 않은 방법
- 문제점
1. 플래그 확인하는 구간이 길어지면 멈추는데 시간이 오래걸릴 수 있다.
2. 하지만 작업 중 블로킹 메소드를 호출하는 부분이 있다면 플래그를 확인할 수 없어 작업이 멈추지 않을 수도 있다.
7.1.1 인터럽트
- 모든 스레드는 인터럽트 상태를 저장하는 boolean 변수가 있다.
- Thread#interrupt로 스레드에 인터럽트 요청을 할 수 있다.
Thread#isInterrupted로 인터럽트 상태 확인 가능
- static 메소드인 Thread.interrupted는 현재 인터럽트 상태를 초기화하기 때문에 주의해야 함
- Thread.sleep, Object.wait 메소드 같은 블로킹 메소드는 인터럽트 상태를 확인해서 인터럽트
걸리면 즉시 리턴됨
(BlockingQueue.take 메소드도 인터럽트 상태를 주기적으로 체크하여 발생 시 InterruptedException을 던진다)
7.1.1 인터럽트
- 스레드에 인터럽트가 걸리면 상태변수가 설정된다.
- 하지만 인터럽트 여부 확인 및 그에 대한 처리는 스레드 내에서 알아서 해야하는 것이다.
- 인터럽트 건다고해서 InterruptedException이 발생하는 것은 아니다.
- 예제 7.5 인터럽트를 사용해 작업을 취소
7.1.2 인터럽트 정책
- 인터럽트에 대한 처리 정책을 세워야 한다.
- 최대한 빠르게 중단시키고 자원정리 및 중단 요청 스레드에게 중단 중 사실을 알려주는게 가장 좋음
- 작업 마무리를 위해서 작업 내에서 단일 연산으로 보호할 수 있는 범위 확인이 필요하다.
- 작업은 스레드 풀과 같이 실행 전담 스레드에 의해 실행되는데, 작업 요청한 스레드에서도 스레드의 인터럽트 상태를 전달받아서 인터럽트 상태에 대응할 수 있도록 해야한다.
(대부분의 블로킹 메소드에서 인터럽트 걸리면 InterruptedException을 던지는 이유)
7.1.2 인터럽트 정책
- 스레드는 해당 스레드를 소유하는 클래스에서만 인터럽트를 걸어야 한다.
- 인터럽트에 대한 처리를 정확히 모르는 스레드에게 함부로 인터럽트를 걸어서는 안됨
- 자바는 선점형 인터럽트를 제공하지 않아 비판적 의견이 있음
- 하지만 인터럽트에 대한 실제 중단 시점을 개발자에 의해 조절할 수 있어 응답성과 안정성을 능동적으로 관리할 수 있는
장점이 있다.
7.1.3 인터럽트에 대한 대응
- InterruptedException이 발생했을 때 처리하는 실적적 방법은 두 가지
1. 발생한 예외를 호출 스택 상위 메소드로 전달
(메소드 선언부에 throws 선언하거나 예외를 다시 throw)
2. 호출 스택 상위 메소드가 처리할 수 있도록 인터럽트 상태 유지
(Thread.currentThread().interrupt() 호출)
- 블로킹 메소드 호출하는 부분이 없다면 작업 중간중간 인터럽트 상태를 확인해야 인터럽트에 대한 응답 속도를 높일 수 있음
- 응답 속도가
중요한 애플리케이션에서는 인터럽트 응답이 늦고 오래 실행되는 메소드 호출은 자제하는 것이 좋음
7.1.4 예제 : 시간 지정 실행
- 실행하는 시간을 지정해서 실행 시간 이후 스레드를 중단시키고자 함
- 예제 7.8 방법은 이해하기도 쉽고 구현하기도 간단함
- 하지만 스레드에 인터럽트를 걸때 대상 스레드 인터럽트 정책을 알고있어야 한다는 규칙을 어기고 있다.
- timeRun 메소드는 외부의 어떤 스레드에서도 호출 가능하기 때문.
- 작업 스레드가
인터럽트에 제대로 반응되지 않도록 되어 있으면 멈추지 않을 수도 있음
7.1.4 예제 : 시간 지정 실행
- 예제 7.8 방법은 이해하기도 쉽고 구현하기도 간단함
- 하지만 스레드에 인터럽트를 걸때 대상 스레드 인터럽트 정책을 알고있어야 한다는 규칙을 어기고 있다.
- timeRun 메소드는 외부의 어떤 스레드에서도 호출 가능하기 때문.
- 작업 스레드가
인터럽트에 제대로 반응되지 않도록 되어 있으면 멈추지 않을 수도 있음
7.1.5 Future를 사용해 작업 중단
- 작업을 중단하려 할 때는 항상 스레드에 직접 인터럽트를 거는 대신 Future.cancel 메소드를 호출해야 한다.
7.1.7 newTaskFor 메소드로 비표준적인 중단 방법 처리
- Interrupt 메소드를 오버라이드해 표준에 정의되어 있지 않은 작업 중단
방법을 구현
7.2 스레드 기반 서비스 중단
- 스레드 기반 서비스를 생성한 메소드 보다 생성된 스레드 기반 서비스가 오래 실행될 수 있는 상황이라면, 스레드 기반 서비스에서는 항상 종료시키는 방법을 제공해야 한다.
- 스레드 풀에 들어 있는 모든 작업 스레드는 해당하는 스레드 풀이 소유한다고 볼 수 있고, 따라서 개별 스레드에 인터럽트를 걸어야 하는 상황이 된다면 그 작업은 스레드를 소유한 스레드 풀에서 책임을 저야한다.
- 애플리케이션이 개별 스레드에 직접 액세스하는 대신 스레드 기반 서비스가 스레드의 시작부터 종료까지 모든 기능에 해당하는
메소드를 직접 제공해야 한다.
7.2 스레드 기반 서비스 중단
- LogWriter에 추가한 안정적인 종료 방법 예제
7.2 스레드 기반 서비스 중단
- 종료된 이후에도 실행이 중단된 작업이 어떤 것인지 알려주는 ExecutorService
예제
7.2.5 shutdownNow 메소드의 약점
- 실행되기 시작했지만 아직 완료되지 않은 작업이 어떤 것인지 알아볼 수 있는 방법이 없다.
- 예제 7.21 종료된 이후 실행 중단 작업을 알려주는 방법
이런 기법이 제대로 동작하려면 개별 작업이 리턴될 때 자신을 실행했던 스레드의 인터럽트 상태를 유지해야 함
7.3 비정상적인 스레드 종료 상황
- UncaughtExceptionHandler 를 지정해두면 처리하지 못한 예외 상황 때문에 스레드가 종료되는 경우에 호출된다.
- 잠깐 실행하고 마는 애플리케이션이 아닌 이상, 예외가 발생했을 때 로그 파일에 오류를 출력하는 간단한 기능만이라도 확보할 수 있도록 모든 스레드를 대상으로 UncaughtExceptionHandler를 활용해야 한다.
7.4 JVM 종료
- 종료 훅
- 예정된 절차대로 종료되는 경우에 Runtime.addShutdownHook 메소드를 사용해 등록된 모든 종료 훅을 실행시킨다.
- 하나의 JVM에 여러 개의 종료 훅을 등록할 수 있다.
- 두 개 이상의 종료 훅이 등록되어 있는 경우에 어떤 순서로 훅을 실행하는지에 대해서는 아무런 규칙이 없다.
- 종료 훅은 스레드에 안전하게 만들어야만 한다.
- 종료 훅은 어떤 서비스나 애플리케이션 자체의 여러 부분을 정리하는 목적으로 사용하기 좋다.
7.4 JVM 종료
- 데몬 스레드
- 비데몬 스레드 중 하나라도 종료되지 않았을 경우만 떠있게 되는 스레드
(데몬 스레드만 존재하면 데몬 스레드들은 종료된다.)
- 데몬 스레드는 finally 블록도 실행되지 않는다.
- 부수적인 작업 처리를 위해 사용(예, 가비지 컬렉터 스레드)
- 스레드 생성 시 부모 스레드의 데몬 여부값을 그대로 사용한다.
7.4 JVM 종료
- finalize 메소드
- 가비지 컬렉터에 수집될 때 객체에 finalize가 정의되어 있으면 호출된다.
- 명시적으로 자원 정리 작업을 하는 용도로 사용
- 실행 여부나 시점에 대한 보장이 없고 성능상 문제가 있을 수 있음
=> try-finally 구문을 대신 사용하고 finalize는 사용하지 말아라
반응형
'개발 > 병렬 프로그래밍' 카테고리의 다른 글
자바 병렬 프로그래밍 - 10장 활동성 최대로 높이기 (0) | 2016.09.01 |
---|---|
자바 병렬 프로그래밍 - 8장 스레드 풀 활용 (0) | 2016.08.30 |
자바 병렬 프로그래밍 - 6장 작업실행 (0) | 2016.08.16 |
자바 병렬 프로그래밍 - 5장 구성 단위 (0) | 2016.08.12 |
자바 병렬 프로그래밍 - 4장 객체 구성 (0) | 2016.07.31 |