「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 12)



Two-Phase Termination 패턴



포인트는
- 안전하게 종료하는 것(안전성)
- 반드시 종료 처리를 실시하는 것(생존성)
- 종료 처리를 내면 가능한 한 빨리 종료 처리에 들어가는 것(응답성)

약 500밀리초 간격으로 카운트 업을 하고 있는 thread를, 약 10초 후에 종료시킨다고 하는 프로그램을 생각한다.

(코드 전체는 본서를 참조)

Main.java
public class Main { 
    public static void main(String[] args) { 
        try { 
            CountupThread t = new CountupThread(); 
            t.start(); 
            Thread.sleep(10000); 
            t.shutdownRequest(); 
            t.join(); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    } 
}

CountupThread.java
public class CountupThread extends Thread { 
    private long counter = 0; 
    private volatile boolean shutdownRequested = false; 

    public void shutdownRequest() { 
        shutdownRequested = true; 
        interrupt(); 
    } 

    ... 

    public final void run() { 
        try { 
            while (!isShutdownRequested()) { 
                doWork(); 
            } 
        } catch (InterruptedException e) { 
        } finally { 
            doShutdown(); 
        } 
    } 

    private void doWork() throws InterruptedException { 
        counter++; 
        System.out.println("doWork: counter = " + counter); 
        Thread.sleep(500); 
    } 

    ... 
}

등장인물



TerminationRequester 역할
TerminationRequester 역할은 Terminator 역할에 종료 요청을 발행합니다. 샘플 프로그램에서는 Main 클래스가 이 역할을 맡았다.

Terminator 역할
Terminator 역할은 종료 요청을 받고 실제로 종료 처리를 수행합니다. Terminator 역할은 종료 요청을 나타내는 shutdownRequest 메서드를 제공합니다. Terminator 역은 자신이 종료 요청을 받았는지 여부를 나타내는 플래그(래치)를 가지고 있다. 샘플 프로그램에서는 CountupThread 클래스가 이 역할을 맡았다.

생각을 넓히는 팁



Thread 클래스의 stop 메소드를 사용해서는 안됩니다.
stop을 사용하면, 크리티컬 섹션의 실행 도중이라도, thread는 java.lang.ThreadDeath 라고 하는 예외를 던져 종료한다.

플래그 테스트만으로는 충분하지 않습니다.
shutdownRequest 메소드 중에서, interrupt 메소드를 호출할 필요가 있는가. shutdownRequest 플래그만으로는 안 되는가. shutdownRequested 플래그만이라면, 예를 들면 thread가 sleep 하고 있으면, 아무리 플래그를 true 로 해도 종료 처리를 시작해 주지 않는다. sleep 시간이 지나면 종료 처리를 시작해 줄지도 모르지만, 그러면 응답성이 나빠져 버린다. interrupt 메소드를 사용하면, sleep나 wait를 중단할 수 있다.

인터럽트 상태 테스트만으로도 불충분
이번에는 반대로, interrupt 메소드를 부르는 것만으로는 안 되는가. 예를 들어, thread가 실행하고 있는 doWork 메소드안에서 다음과 같이 기술해 버리면 잘 안된다.
    private void doWork() throws InterruptedException { 
        counter++; 
        System.out.println("doWork: counter = " + counter);
        try { 
            Thread.sleep(500);
        } catch (InterruptedException e) {
        } 
    }

interrupt 메소드 정보
interrupt 메소드를 호출하면(자), 대상의 thread는 항상 InterruptedException 를 던질 것이라고 생각하고 있을지도 모르지만, 그것은 오해이다. interrupt라는 메소드는, thread의 인터럽트 상태를 바꾸는 것 뿐이다. 인터럽트 상태란, thread가 인터럽트 되고 있는지 어떤지를 나타내고 있는 상태를 말한다. sleep, wait, join이 InterruptedExcpetion을 던지는 이유는 메소드 내부에서 스레드의 인터럽트 상태를 조사하고 명시적으로 InterruptedException을 던지고 있기 때문이다. wait, sleep, join로 일단 InterruptedException가 던지면, thread는 인터럽트 상태가 아니게 된다.

관련
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 1)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 2)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 3)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 4)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 5)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 6)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 7)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 8)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 9)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 10)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 11)

좋은 웹페이지 즐겨찾기