Hello, Freakin world!

[Java] Thread를 종료하는 방법 두 가지 본문

프로그래밍 언어/Java

[Java] Thread를 종료하는 방법 두 가지

johnna_endure 2020. 4. 2. 20:25

Thread api 중 destroy, stop, suspend 등이 안전 상의 이유로 폐기됐습니다.

스레드를 강제로 종료시키던 destroy()는 사전에 자원의 cleanup 작업을 하지 않습니다. 그래서 때때로 monitor가 락을 유지한 상태로 내버려두기도 해, 데드락 유발의 원인이었다고 하네요.

https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

 

Java Thread Primitive Deprecation

Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated? Why is Thread.stop deprecated? Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlo

docs.oracle.com

위 사이트를 보시면 oracle에서 stop, suspend 등의 메서드들을 왜 폐기했는지, 대안은 무엇인지에 대해 설명합니다.

아래 설명들은 위 사이트를 참고했으므로, 더 자세한 설명은 위 사이트를 참고하세요.

 

이제 스레드를 종료하는 방법 두 가지를 알아보겠습니다.

 

1. volatile 변수를 이용한 방법

2. Interrupt 이용한 방법


1. volatile 변수를 이용한 방법

 

이 경우는 스레드가 블락되지 않는 경우에 유효합니다.

public class WorkThread extends Thread{
    volatile boolean terminationFlag = false;

    @Override
    public void run() {
        while(terminationFlag) {
            System.out.println("스레드 돌아가는 중");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void stopThread() {
        terminationFlag = false;
    }
}

 

volatile 변수를 이용한 이유는 스레드 종료 요청이 빠르게 전달되기를 보장하기 위함입니다.

일반 변수를 사용하게 되면 while문 조건의 terminationFlag와 stopThread 메서드 내의 terminationFlag가 경쟁 상태에 들어가게 될 수 있습니다. 그로인해 어떤 경우엔 루프를 한번 더 돈 후에 종료될 겁니다.

 

 

2. Interrupt 이용한 종료

 

만약 while 루프 중 스레드가 블락되거나 대기상태라면 어떡할까요?

1번의 방법으로 while 문을 빠져나갈 수 없습니다.

 

이럴 때! interrupt를 이용해야 합니다.

외부 스레드가 이 스레드의 interrupt 메서드를 이용해 인터럽트 시킬 때 던지는 예외를 처리함으로서 스레드를 종료시킬 수 있습니다.

 

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class WaintingWorkThread extends Thread{
    ServerSocketChannel serverSocketChannel;
    SocketChannel socketChannel;

    @Override
    public void run() {
        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress("localhost", 9002));
            while(true) {
                System.out.println("accept 작업으로 인한 대기");
                socketChannel = serverSocketChannel.accept();
                System.out.println("대기 해제.");
            }
        } catch (ClosedByInterruptException e) {
            System.out.println("인터럽트로 인한 스레드 종료.");
            return;
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("스레드 종료.");
    }

    public void stopThread() {
        super.interrupt();
    }
}

 

public class Main {
    public static void main(String[] args) {
        WaitingWorkThread waitingWorkThread = new WaitingWorkThread();
        waitingWorkThread.start();
        
        waitingWorkThread.stopThread();
    }
}

 

위의 예제에서는 편의를 위해서 Main에서 인터럽트시킨 후 ClosedByInterruptException 예외를 던지는 것을 확인하고 바로 ClosedByInterruptException 예외를 catch 해서 처리하고 있습니다.

 

하지만 실제로 사용할 땐 serverSocketChannel.accept를 다른 메서드로 빼내고 메서드 내에서 ClosedByInterruptException를 InterruptException으로 감싸서 던지게 한 후, run 메서드에서 InterruptException를 캐치하도록 할 것 같네요.  

 

Comments