(서비스 제공자가 주도하는) 자바 인터럽트

주싱·2023년 3월 16일
0

더 나은 도구

목록 보기
7/13

Image by pixabay

자바의 인터럽트 기능(Thread.Interrupt())을 사용하면 Blocking 동작이 있는 대상 쓰레드의 동작을 안전하게 중단시킬 수 있다. 그러나 인터럽트 기능은 요청자 주도로 대상 쓰레드 실행을 임의로 중단시킬 수 있는 기능이 아니다. 인터럽트는 단지 대상 쓰레드에게 실행을 중단해 줄 것을 요청하며 인터럽트 대상 쓰레드가 실행하는 서비스 구현에서 인터럽트를 어떻게 처리할지 주도해서 결정할 수 있다. 우리가 직접 API 서비스를 구현한다면 인터럽트 할 수 없도록(Uninterruptibly) 구현할 수도 있고, 인터럽트를 받아서 처리해 주도록(Interruptibly) 구현할 수도 있다. 대기 동작을 가지는 많은 자바 API는 대기 동작을 중단하고 InterruptedException을 던져준다.

인터럽트를 무시하는 구현

다음 샘플 코드는 인터럽트를 요청하더라도 서비스 구현에서 인터럽트를 무시하고 실행을 이어가도록 작성한 예를 보여준다.

@Test
void interruptWithoutHandling() throws InterruptedException {
    AtomicBoolean stopSwitch = new AtomicBoolean(false);
    AtomicLong counter = new AtomicLong(0);

    // When: 인터럽트를 처리하지 않는 쓰레드 생성
    Thread thread = new Thread(() -> {
        while(!stopSwitch.get()) {
            counter.incrementAndGet();
        }
    });
    thread.start();

    // When: 쓰레드 인터럽트
    thread.interrupt();

    // Then: 쓰레드는 인터럽트 플래그가 설정된 상태로 살아서 실행을 이어간다.
    await().during(3, TimeUnit.SECONDS)
            .until(() -> thread.isInterrupted() &&
                    thread.isAlive() &&
                    runningThread(counter, 100)
            );
}

private boolean runningThread(AtomicLong counter, long checkInterval) throws InterruptedException {
    long before = counter.get();
    Thread.sleep(checkInterval);
    long after = counter.get();
    return before != after;
}

인터럽트를 받아 처리하는 구현

반면 다음 샘플 코드는 인터럽트를 체크해서 작업을 중단해 주는 서비스 코드의 예를 보여준다.

@Test
void interruptWithHandling() throws InterruptedException {
    AtomicLong counter = new AtomicLong(0);

    // When: 인터럽트를 처리하는 쓰레드 생성
    Thread thread = new Thread(() -> {
        while(!Thread.interrupted()) { // 인터럽트가 발생하면 인터럽트 플래그를 초기화하고 true를 반환한다.
            counter.incrementAndGet();
        }
        Thread.currentThread().interrupt(); // 인터럽트 플래그를 다시 설정한다. (외부에 인터럽트에 반응 했음을 알림)
    });
    thread.start();

    // When: 쓰레드 인터럽트
    thread.interrupt();

    // Then: 쓰레드는 인터럽트 되고, 실행을 중료한다.
    await().atMost(1, TimeUnit.SECONDS)
            .until(() -> thread.isInterrupted() &&
                    !thread.isAlive() &&
                    !runningThread(counter, 100)
            );
}

전체 코드는 GitHub에서 확인할 수 있습니다.

profile
소프트웨어 엔지니어, 일상

0개의 댓글