본문 바로가기

Back/Java

[JAVA/멀티스레드] JAVA멀티스레딩 - 스레드조정 (스레드 종료 및 Demon 스레드)

 

스레드 조정

하나의 스레드를 다른 스레드에서 멈추게하는 작업
 
 

스레드를 멈추게 하는 이유 (스레드 조정 이유)와 스레드 조정방법 (멈추는 방법)

1. 사용하지 않는 스레드의 리소스 정리

  • 스레드는 아무것도 하지 않을때도 리소스를 사용한다.
  • 메모리와 일부 커널 리소스를 사용하고, 스레드가 실행중이라면 CPU시간 뿐만 아니라 CPU 캐시공간도 사용한다.
  • 그러므로 생성한 스레드가 이미 작업을 완료했는데, 애플리케이션은 작동 중이라면 사용하지 않는 스레드가 사용하는 리소스는 정리해야 한다.


예제

스레드 sleep메서드에서는 interruptedException이 발생하면 직접 처리해야 하는게 명확하다

package thread.interrupt;

public class Main {
   public static void main(String[] args) {
      Thread thread = new Thread(new BlockingTasks());
      thread.start();
      // 이 스레드가 인터럽트 될때, 예외처리 발생
      thread.interrupt();
   }
   private static class BlockingTasks implements Runnable {
      @Override
      public void run() {
         try {
            Thread.sleep(50000);
         } catch (InterruptedException e) {
         	//아래 내용 출력 후 스레드 바로 종료
            System.out.println("Existing blocking thread");
         }
      }
   }
}

 

2. 스레드가 오작동할때

  • 응답이 없는 서버에 계속 요청을 보내는 경우
  • 우리가 허용하는 것보다 오래 긴 계산을 전송하고 있을 경우 등
  • 어떤식으로든 스레드가 오작동한다면 중지해야한다.

예제

package thread.interrupt;

import java.math.BigInteger;

public class Main {
   public static void main(String[] args) {
      Thread thread = new Thread(new LongComputationTasks(new BigInteger("2"), new BigInteger("10")));
      thread.start();
   }

   private static class LongComputationTasks implements Runnable {
      private BigInteger base;
      private BigInteger power;

      public LongComputationTasks(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.add(BigInteger.ONE)) {
            result = result.multiply(base);
         }
         return result;
      }
   }
}
  • base와 power의 값이 많이 커지게 되면, 예를 들어 20000^1000000000
  • 계산이 끝날때까지 기다리거나 스레드를 인터럽트해서 앱을 종료해야한다.
  • 인터럽트를 해도, 이를 처리할 메서드나 로직이 없기 때문에 LongComputation은 계속 돌아간다
  • 이런 경우, 코드 내에서 시간이 오래 걸리는 핫스팟을 찾아서 외부에서 인터럽트를 당했는지는지를 확인한다. 
private BigInteger pow(BigInteger base, BigInteger power) {
   BigInteger result = BigInteger.ONE;
   for (BigInteger i = BigInteger.ZERO; i.compareTo(power) != 0; i.add(BigInteger.ONE)) {
      if (Thread.currentThread().isInterrupted()) {
         System.out.println("Prematurely interrupted computation");
         return BigInteger.ZERO;
      }
      result = result.multiply(base);
   }
   return result;
}

 

 

3. 애플리케이션 전체를 중단하기 위해

  • 메인스레드가 종료됐어도 최소 하나의 스레드가 실행되고 있다면 애플리케이션은 종료되지 않는다
  • 그래서 애플리케이션을 끝내기전에 모든스레드를 중단할 기능이 필요
  • 이럴때 데몬스레드가 필요하다.

 

Demon threads

  • 배경에서 실행되는 스레드
  • 앱의 주작업이 아닌 백그라운드 작업을 담당
  • 텍스트에디터 프로그램의 경우, 몇분마다 작업을 파일에 저장하는 스레드
  • 데몬스레드는 애플리케이션의 종료에 영향을 줄수 없다.
  • 데몬스레드가 끝나지않아도 메인 스레드가 끝나면 애플리케이션이 종료된다.


예제

  • Daemon 속성을 'true'로 설정해서 스레드를 데몬스레드로 지정할 수 있다.
  • thrad.setDaemon(true);
  • 메인 스레드가 종료됐다는 것만으로 전체 앱이 종료되는걸 볼 수 있다