쓰레드 interrupt

류홍규·2023년 6월 23일
0

JAVA

목록 보기
5/7
post-thumbnail

✅ 쓰레드 조정

💎 Thread Termination - Why and When?

  • Threads consume resources
    • Memory and kernel resources
    • CPU cycles and cache memory
  • 쓰레드 작업이 끝난 후에도 애플리케이션이 실행중이라면, 쓰레드 자원을 치워야한다.
  • 쓰레드가 잘못동작한다면, 쓰레드를 중단해야한다.
  • 쓰레드가 하나라도 동작하고 있다면, 애플리케이션은 끝나지 않는다.

⚾ Thread.interrupt()

  1. If the thread is executing a method that throws an InterruptedException
  2. If the thread’s code is handling the interrupt signal explicitly(인터럽트하려는 쓰레드가 신호를 명시적으로 처리하는 경우)
package thread.interrupt;

public class example {
    public static void main(String[] args) {
        Thread thread = new Thread(new BlockingTask());

        thread.start();

        thread.interrupt();
    }

    private static class BlockingTask implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                System.out.println("블로킹 쓰레드 종료");
            }
        }
    }
}
  • thread.interrupt를 호출해 스레드를 정상적으로 멈추는 방법
  1. 인터럽트 신호에 명시적으로 반응하는 메서드를 쓰지 않을 때는, InterruptException을 발생시켜 신호를 직접 처리해야한다.
  2. 애플리케이션을 Daemon쓰레드로 설정해 스레드가 앱의 종료를 방해하지 않는 방법을 설정할 수 있다.

[상황]

사용자가 아주 큰 숫자를 주면 계산이 끝날 때까지 기다려야한다.

  • 따라서 스레드를 인터럽트해서 앱을 종료할 수 있게끔 설정한다.
  • 이때, thread.interrupt()신호를 보냈을 때 이를 처리할 메서드와 로직을 설정해주어야한다.
    • 시간이 가장 오래 걸리는 로직을 찾는다.
    • Thread.currentThread().isInterrupted()를 통해 앱을 종료할 수 있게끔 설정한다.
package thread.interrupt;

import java.math.BigInteger;

public class example2 {
    public static void main(String[] args) {
        Thread thread = new Thread(new LongComputationTask(new BigInteger("20000000"), new BigInteger("10000000000")));

        thread.start();
        thread.interrupt(); // 인터럽트는 보내졌지만, 이를 처리할 메서도느 로직이 없기 때문
    }
    private static class LongComputationTask implements Runnable{
        private BigInteger base;
        private BigInteger power;

        public LongComputationTask(BigInteger base, BigInteger power) {
            this.base = base;
            this.power = power;
        }
        @Override
        public void run() {
            System.out.println(base+"^"+power+"="+pow(base, power));
        }
        private BigInteger pow(BigInteger base, BigInteger power) {
            BigInteger result = BigInteger.ONE;

            for(BigInteger i=BigInteger.ZERO; i.compareTo(power) != 0; i=i.add(BigInteger.ONE)) {
                // 해당 스레드가 외부에서 인터럽트 당했는지 확인하는
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("계산을 종료해라!");
                    return BigInteger.ZERO;
                }
                result = result.multiply(base);
            }
            return result;
        }
    }
}

[출력]

계산을 종료해라!
20000000^10000000000=0

💎 Daemon Thread

데몬 쓰레드는 백그라운드에서 실행되는 쓰레드로, 메인쓰레드가 종료되어도, 애플리케이션의 종료를 막지 않는다.

  • 몇몇 쓰레드를 데몬쓰레드로 설정하면 앱의 주 작업이 아닌 백그라운드 작업을 맡는다.
  • example) File saving thread in a Text editor
    • main thread: saving thread
    • Daemon thread: 사용자가 갑자기 앱을 종료할 경우 백그라운드 스레드의 실행여부는 신경쓰지 않고 완료될 때까지 기다리지도 않는다.
package thread.interrupt;

import java.math.BigInteger;

public class example2 {
    public static void main(String[] args) {
        Thread thread = new Thread(new LongComputationTask(new BigInteger("20000000"), new BigInteger("10000000000")));
        thread.setDaemon(true);
        thread.start();
        thread.interrupt(); // 인터럽트는 보내졌지만, 이를 처리할 메서도느 로직이 없기 때문
    }
    private static class LongComputationTask implements Runnable{
        private BigInteger base;
        private BigInteger power;

        public LongComputationTask(BigInteger base, BigInteger power) {
            this.base = base;
            this.power = power;
        }
        @Override
        public void run() {
            System.out.println(base+"^"+power+"="+pow(base, power));
        }
        private BigInteger pow(BigInteger base, BigInteger power) {
            BigInteger result = BigInteger.ONE;

            for(BigInteger i=BigInteger.ZERO; i.compareTo(power) != 0; i=i.add(BigInteger.ONE)) {
                // 해당 스레드가 외부에서 인터럽트 당했는지 확인하는
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("계산을 종료해라!");
                    return BigInteger.ZERO;
                }
                result = result.multiply(base);
            }
            return result;
        }
    }
}

[출력]

계산을 종료해라!

해당 쓰레드를 데몬쓰레드로 설정했기 때문에, 애플리케이션이 바로 종료되는 것을 확인할 수 있다

[내용점검]

💡 해당 애플리케이션을 종료시킬려면, 어떻게 해야할까?

public static void main(String [] args) {
        Thread thread = new Thread(new WaitingForUserInput());
        thread.setName("InputWaitingThread");
				[정답]**thread.setDaemon(true);**
        thread.start();
    }
 
    private static class WaitingForUserInput implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    char input = (char) System.in.read();
                    if(input == 'q') {
                        return;
                    }
                }
            } catch (IOException e) {
                System.out.println("An exception was caught " + e);
            };
        }
    }
  • 사용자가 문자 ‘q’를 입력한다.
    • WaitingForUserInput 스레드가 start(); 되기 전에
    • main메서드에 thread.setDaemon(true);로 설정
    • 강제로 애플리케이션 종료
  • System.in.read()는 Thread.interrupt();에 응답하지 않는다.

💡 thread.interrupt(); 를 호출했을 때, catch 블록을 빈 상태로 두어서는 안된다. 해당 로직이나 메서드를 완성해야 현재 스레드를 중단할 수 있다.

public static void main(String [] args) {
        Thread thread = new Thread(new SleepingThread());
        thread.start();
        thread.interrupt();
    }
 
    private static class SleepingThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000000);
                } catch (InterruptedException e) {
									[정답]**return; // 해당 부분을 추가해야줘야, 애플리케이션의
													//종료가 가능하다.**
                }
            }
        }
    }
profile
공대생의 코딩 정복기

0개의 댓글