JAVA

스레드 인터럽트

silver-w 2024. 10. 31. 23:06

1. Interrupt


Interrupt - 1. 인터럽트를 사용하지 말고, 무한루프 스레드의 작업을 중단시키는 방법

 


 아래  work 스레드를 중단하는데에 가장 쉬운 방법은 work스레드 작업에 변수를 지정하고,

실행하는 메인 스레드에서 그 변수를 조작하는 것이다 [ while(true) -> while(false) ]

그런데, 이런 방법은 sleep으로 초를 맞춰서 중단시키는거라,

특정스레드가 끝나자마자 바로 중단하는게 아니라 sleep()이 있으면 sleep()이 끝난다음에 중단을 할 수 있다.

↓  work 스레드 2번째 작업 시작하고 1초 지난 뒤(main스레드가 sleep(4000)에서 깨어나는 시점)에 while(false)를 인식하고 작업이 끝나면(2초뒤에) work 스레드가 작업을 끝냄

21:46:07.644 [ Thread-0] 작업 중
21:46:10.651 [ Thread-0] 작업 중
21:46:11.641 [     main] 작업 중단 지시 runFlag = false
21:46:13.652 [ Thread-0] 자원 정리
21:46:13.653 [ Thread-0] 지원 종료

 

 

Interrupt - 2. sleep()상태에서의 Interrupt


인터럽트를 사용하면 WAITING TIMED_WAITING 같은 대기 상태의 스레드를 직접 깨워서 RUNNABLE 상태로 만들 수 있다

public class ThreadStopMainV2 {
    public static void main(String[] args) {
        MyTask task = new MyTask();
        Thread thread = new Thread(task);
        thread.start();

        sleep(4000);
        log("작업 중단 지시 thread.interrupt()");
        thread.interrupt();
        log("work 스레드 인터럽트 상태1 = " + thread.isInterrupted());
    }

    static class MyTask implements Runnable {
        @Override
        public void run() {
            try {
                while(true) {
                    log("작업 중");
                        Thread.sleep(3000); // 여기서 interrupt 발생
                    }
            } catch (InterruptedException e) {
                log("work 스레드 인터럽트 상태2 = " + Thread.currentThread().isInterrupted());
                log("interrupt message=" + e.getMessage());
                log("state = " + Thread.currentThread().getState());

            }
            log("자원 정리");
            log("지원 종료");
        }
    }
}

 

 public static void main(String[] args) {
        ~~~~~
        log("작업 중단 지시 thread.interrupt()");
        mytaskThread.interrupt();
        log("work 스레드 인터럽트 상태1");
    }
 static class MyTask implements Runnable {
        @Override
        public void run() {
            try {
                while(true) {
                    log("작업 중");
                    Thread.sleep(3000);
                }
            } catch (InterruptedException e) {
                log("work 스레드 인터럽트 상태2 ")
                log("interrupt message=" + e.getMessage());
                log("state = " + Thread.currentThread().getState());
            }
            log("자원 정리");
            log("지원 종료");
        }
    }
22:07:53.172 [ Thread-0] 작업 중                                                           - 0초
22:07:56.176 [ Thread-0] 작업 중                                                           - 3초 
22:07:57.164 [     main] 작업 중단 지시 thread.interrupt()                      - 4초 : mytaskThread.interrupt();
22:07:57.176 [     main] work 스레드 인터럽트 상태1 = true                   ㄴ myTask 스레드가 sleep(3000) 도중 인터럽(예외발생)
22:07:57.176 [ Thread-0] work 스레드 인터럽트 상태2 = false               ㄴ 대기상태에서 깨어나 즉각 Runnable상태가 된다.
22:07:57.177 [ Thread-0] interrupt message=sleep interrupted
22:07:57.178 [ Thread-0] state = RUNNABLE
22:07:57.178 [ Thread-0] 자원 정리
22:07:57.179 [ Thread-0] 지원 종료
  • interrupt()는 waiting 이나 timed waiting인 스레드 상태를 runnable로 바꾼다, 
  •  MyTask 대상 스레드인터럽트 상태 (isInterrupted() = true) 가 되었고,
    sleep()을 진행할 때 InterruptException이 터진다
    이때  MyTask 대상 스레드의 인터럽트 상태는 종료 (isInterrupted = false) 가 되고, Runnable상태가 된다. 
    [ 예외가 터짐으로 반복문에서 탈출하고, catch 이후 코드를 진행하게 된다. ]
  • 메인 스레드에서 MyTask 대상 스레드의 필드로 작업을 조작하는것 보다 더 빠르게 작업을 중단할 수 있다.

 

interrupt - 3. 조건문에 isInterrupted()를 이용하여 인터럽걸기


 

public class ThreadStopMainV3 {
    public static void main(String[] args) {
        ~~~~~~
        log("작업 중단 지시 thread.interrupt()");
        thread.interrupt();
        log("work 스레드 인터럽트 상태1 = " + thread.isInterrupted());
}

static class MyTask implements Runnable {

        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()) {
                // 인터럽트 상태가 false라면 집행
                log("작업 중");
            }
            log("work 스레드 인터럽트 상태2 = " + Thread.currentThread().isInterrupted());
			~~~~~
        }
    }
22:41:17.649 [ Thread-0] 작업 중
22:41:17.649 [     main] 작업 중단 지시 thread.interrupt()
22:41:17.649 [ Thread-0] 작업 중
22:41:17.655 [     main] work 스레드 인터럽트 상태1 = true
22:41:17.655 [ Thread-0] work 스레드 인터럽트 상태2 = true
22:41:17.655 [ Thread-0] 자원 정리
22:41:17.655 [ Thread-0] 지원 종료

 - sleep() 코드가 MyTask run()메서드에 존재하지 않아, InterruptException이 발생치 않는다

=> isInterrupted가 항상 true가 된다. 해당 Interrupted 상태는 sleep()같은 코드를 만나지 않는 한 유지된다.

 

이는 의도치 않은 interruptedException을 초래할 수도 있다. 

스레드의 무한루프를 빠져나가지기 위해서 main 스레드에서 mytask 스레드의 interrupt을 요청했는데, while만 빠져나가기 까지는 잘했지만, interrupt 상태는 계속 true라서, 이를 생각도 못하고 나중에 myTask에서 sleep() 코드를 이어 적으면 interruptException이 터져서 원치 않는 결과가 나온다 

=> 그러므로 interrupt 목적을 달성하면 isInterrupted=false로 돌려놔야한다.

 

Interrupt - 4. Thread.interrupted()


isInterrupted()            : 단순히 인터럽 상태를 확인하는 용도
Thread.interrupted()   : 직접 인터럽 상태를 체크해서 사용할 때 사용 
                                       스레드가 인터럽트 상태라면 true를 반환하고, 상태를 false로 변경한다.
                                       만약 현재 인터럽트 상태가 false라면 상태를 유지한다

 

23:00:42.277 [ Thread-0] 작업 중
23:00:42.277 [     main] 작업 중단 지시 thread.interrupt()
23:00:42.277 [ Thread-0] 작업 중
23:00:42.284 [     main] work 스레드 인터럽트 상태1 = true
23:00:42.284 [ Thread-0] work 스레드 인터럽트 상태2 = false
23:00:42.285 [ Thread-0] 자원 정리
23:00:42.285 [ Thread-0] 지원 종료

 

 

※ 참고로 Thread.stop() 이라는 옛날 메서드가 있지만, 스레드 자원반납, 마무리 등에 문제가 있어 더 이상 사용하지 않음

 


출처 https://inf.run/NC7kS

 

김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성 강의 | 김영한 - 인프런

김영한 | 멀티스레드와 동시성을 기초부터 실무 레벨까지 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을 안다?

www.inflearn.com