일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스택
- Logback
- 달팽이
- Gradle
- 비트마스킹
- spring cloud
- BFS
- 서비스 디스커버리
- 스프링 시큐리티
- docker-compose
- 구간 트리
- 완전 탐색
- 이분 매칭
- dp
- Zuul
- 플로이드 와샬
- Spring Cloud Config
- 게이트웨이
- 주울
- Java
- 도커
- 유레카
- ZuulFilter
- spring boot
- 트리
- 다익스트라
- 백트래킹
- 이분 탐색
- 메모이제이션
- 구현
- Today
- Total
Hello, Freakin world!
[Java] Thread를 종료하는 방법 두 가지 본문
Thread api 중 destroy, stop, suspend 등이 안전 상의 이유로 폐기됐습니다.
스레드를 강제로 종료시키던 destroy()는 사전에 자원의 cleanup 작업을 하지 않습니다. 그래서 때때로 monitor가 락을 유지한 상태로 내버려두기도 해, 데드락 유발의 원인이었다고 하네요.
https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
위 사이트를 보시면 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를 캐치하도록 할 것 같네요.
'프로그래밍 언어 > Java' 카테고리의 다른 글
[vavr] try..catch를 함수형 인터페이스 Try로 처리 (0) | 2021.03.09 |
---|---|
[Java] if문이 안티패턴이 되는 경우에 대해 (0) | 2020.04.09 |
[Java] volatile에 대한 고찰(용도, 스레드 세이프, 실제적 쓰임) (0) | 2020.04.02 |
[Java][Reflection] Method 인수의 name 속성 알아내기 (0) | 2020.03.31 |
[Java] gradle 프로젝트의 컴파일 옵션 추가하기 (0) | 2020.03.30 |