JAVA/멀티스레딩

스레드 조정

최-코드 2024. 8. 28. 11:02

스레드 조정

  • 하나의 스레드를 다른 스레드에서 종료시키는 작업이다.
  • 왜 필요한가 - 스레드는 아무 것도 하지 않을 때에도 리소스를 차지한다.
  • 언제 필요한가
    • 스레드가 오작동할 때 -> 응답 없는 서버에 계속 요청 보내기와 같은 상황 or 허용한 시간보다 오래 동작되는 상황
    • 애플리케이션을 종료하려고 할 때 -> main thread가 이미 종료되었다 하더라도 스레드가 남아 있다면 종료가 안 된다.

Thread.interrupt() 

  • 각 스레드 객체는 interrupt라는 메소드르 가진다.
  • threadA에서 threadB.interrupt()를 하면 threadA에서 threadB에 인터럽트 신호를 보내게 된다.
  • 이 신호는 스레드가 일시정지 상태(sleep)에 빠져있으면 바로 InterruptException을 발생시키지만, 일시정지가 아닌 상태에서는 아무 효과가 없다.
  • 하지만 신호는 일단 스레드에 전달된 상태이므로 sleep없이 interrupt 신호를 확인할 수 있는 isInterupted() 메소드가 true를 반환하게 된다.
// interrupt 예시 1
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(500000);
        } catch (InterruptedException e) {
            System.out.println("Exiting blocking thread");
        }
    }
}
// interrupt 예시 2
public static void main(String[] args) {
    Thread thread = new Thread(new LongComputationTask(new BigInteger("200000"), new BigInteger("10000000")));
    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("Prematurely interrupted computation");
                return BigInteger.ZERO;
            }
            result = result.multiply(base);
        }
        return result;
    }
}

 

Daemon Thread

  • 백그라운드에서 실행되는 스레드로 이 스레드가 실행되고 있어도 메인 스레드가 종료되면 애플리케이션 또한 종료된다.
  • 앱의 주 작업이 아닌 백그라운드 작업을 맡길 때 사용한다.
  • 예를 들어 텍스트 편집기에서 몇 분마다 작업을 파일레 저장하는 스레드가 있다. 이 때 우리는 앱을 닫고 싶을 때 백그라운드 스레드의 실행여부를 신경 쓰지 않고, 완료될 때까지도 기다리지 않는다.
public static void main(String[] args) {
    Thread thread = new Thread(new LongComputationTask(new BigInteger("200000"), new BigInteger("10000000")));
    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)){
            result = result.multiply(base);
        }
        return result;
    }
}

 

join()

  • 스레드가 끝날 때까지 기다려주는 메소드이다.
  • 특정 스레드 A에 대해 main스레드에서 threadA.join()와 같이 하면 threadA의 스레드가 종료될 때까지 join()을 호출한 라인에서 계속 기다리게 된다.
  • join 메소드를 호출한 스레드는 블로킹 상태로 변하게 되는데, 스레드 스케줄링 대상이 되지 않으므로 cpu 시간을 갖지 않아 다른 연산 중인 스레드의 연산을 더욱 빨리 끝내줄 수 있다.
  • 이 때 밀리초 인자를 주어서 스레드가 해당 밀리초를 넘도록 종료되지 않으면 이 메소드를 호출한 스레드가 실행 상태로 변하게 된다.
public static void main(String[] args) throws InterruptedException {
    List<Long> inputNumbers = Arrays.asList(1000000000L, 3435L, 35435L, 2324L, 4656L, 23L, 2435L, 5566L);
    List<FactorialThread> threads = new ArrayList<>();

    for (Long inputNumber : inputNumbers) {
        threads.add(new FactorialThread(inputNumber));
    }

    for (FactorialThread thread : threads) {
        thread.setDaemon(true);
        thread.start();
    }
    for (FactorialThread thread : threads) {
        thread.join(1000);
    }
    for (int i = 0; i < inputNumbers.size(); i++) {
        FactorialThread factorialThread = threads.get(i);
        if (factorialThread.isFinished()) {
            System.out.println("Factorial of " + inputNumbers.get(i) + " is " + factorialThread.getResult());
        } else {
            System.out.println("the calculation for " + inputNumbers.get(i) + " is still in progress");
        }
    }
}

public static class FactorialThread extends Thread {
    private long inputNumber;
    private BigInteger result = BigInteger.ONE;
    private boolean isFinished = false;

    public FactorialThread(long inputNumber) {
        this.inputNumber = inputNumber;
    }

    @Override
    public void run() {
        this.result = factorial(inputNumber);
        this.isFinished = true;
    }

    private BigInteger factorial(long n) {
        BigInteger tempResult = BigInteger.ONE;
        for (long i = n; i > 0; i--) {
            tempResult = tempResult.multiply(new BigInteger(Long.toString(i)));
        }
        return tempResult;
    }

    public BigInteger getResult() {
        return result;
    }

    public boolean isFinished() {
        return isFinished;
    }
}