[CS/운영체제] 멀티스레드와 동시성 - 5부

황제연·2025년 6월 28일
0

CS학습

목록 보기
119/193
post-thumbnail

특정 스레드의 작업을 중간에 중단하려면?

public class ThreadTest{

	public staic void main(String[] args){
	
		TestTask task = new TestTask();
		Thread thread = new Thread(task, "works");
		thread.start();

		sleep(5000);
		task.flag = false;
	}

	static class TestTask implements Runnable {
		volatile boolean flag = true;

		@Override
		public void run(){
			while(flag){
				sleep(4000);
			}
			System.out.println("자원 정리 및 종료");
		}
	
	}
	

}

위와같이 flag를 사용한다면 작업을 중단할 수 있습니다

하지만 이미 sleep을 진행중인 스레드는 깨울 수 없습니다.
아직 sleep 상태가 1초라면 3초를 더 기다려야하는 상황입니다

interrupt()

sleep이 끝날 떄까지 기다릴 필요 없이 스레드를 깨우고 싶을 떄 사용하는 것이 interrupt()입니다

public class ThreadTest{

	public staic void main(String[] args){
	
		TestTask task = new TestTask();
		Thread thread = new Thread(task, "works");
		thread.start();

		sleep(5000);
		thread.interrupt();
		System.out.println("인터럽트 상태1 = ", thread.isInterrupted());
	}

	static class TestTask implements Runnable {

		@Override
		public void run(){
			try{
				while(true){
					System.out.println("작업 중");
					Thread.sleep(4000);
				}
			}catch (InterruptedException e){
				System.out.println("인터럽트 상태2 = ", Thread.currentThread().isInterrupted());
				System.out.println("현재상태 = ", Thread.currentThread().getState());
			}
			
			System.out.println("자원 정리 및 종료");
		}
	
	}
	

}

이렇게 interrupt()를 사용하면, 작업이 sleep() 상태에 빠졌을 때, 거의 즉각적으로 깨울 수 있습니다
인터럽트를 받은 스레드는 RUNNABLE 상태가 되고 다음 코드를 정상 수행합니다

interrupt()를 받아도 즉각 실행되는 것은 아니고, 오로지 sleep()처럼 InterruptedException을
던지는 메소드를 호출하거나 호출중일 때 예외가 발생합니다

sleep()에서 interrupt가 발생하면 TIMED_WAITING 에서 RUNNABLE 상태로 변경되면서
InterruptedException 예외가 발생합니다

참고로 catch가 실행되려면 RUNNABLE 상태가 되어야 합니다

catch에서는 interrupt 상태가 true이고, main thread에서 다음 코드를 이어 실행할 때는
스레드의 인터럽트가 종료되고 다시 동작하는 스레드가 되면서 false가 됩니다

개선점

하지만 여전히, sleep 전까지는 interrupt가 즉각 발생하지 않는다는 불편함이 있습니다

public class ThreadTest{

	public staic void main(String[] args){
	
		TestTask task = new TestTask();
		Thread thread = new Thread(task, "works");
		thread.start();

		sleep(5000);
		thread.interrupt();
		System.out.println("인터럽트 상태1 = ", thread.isInterrupted());
	}

	static class TestTask implements Runnable {

		@Override
		public void run(){
			while(!Thread.currentThread().isInterrupted()){
				System.out.println("작업 중");		
			}
			try{
				Thread.sleep(1500);
			}catch (InterruptedException e){
				System.out.println("자원정리 실패");
			}
			
			System.out.println("자원 정리 및 종료");
		}
	
	}
	

}

이번에는 이렇게 바꿔주었습니다.
이제 while문 내에서 interrupt 여부를 체크해서 바로 작업을 중단하도록 설정했습니다

하지만 이 코드는 한가지 문제점이 있습니다
바로 interrupt의 상태가 예외 발생 전까지는 true로 고정된다는 것입니다

그렇기 때문에 while문을 건너 뛰고나서 Thread.sleep()을 실행할 때, 인터럽트 상태이므로
예외가 발생하면서 자원정리 실패를 출력하게 됩니다

바로 이러한 이유 떄문에 자바에서 인터럽트 예외가 한번 발생하면,
스레드의 인터럽트 상태를 다시 정상인 false로 돌립니다

스레드의 인터럽트 상태를 정상으로 돌리지 않으면 이후에도 계속 인터럽트가 발생하므로
인터럽터의 목적을 달성하면 인터럽트 상태를 다시 정상으로 바꿔야 합니다

해결책

앞서 isInterrupted()를 호출해서 단순히 인터럽트 상태를 확인만 했습니다
직접 체크해서 사용하고 싶을 때는 Thread.interrupted()를 사용하면 됩니다

이 메소드는 스레드가 인터럽트 상태라면 true를 반환하고 false로 변경합니다
인터럽트 상태가 아니라면 false를 반환하고 해당 스레드의 인터럽트 상태를 변경하지 않습니다

public class ThreadTest{

	public staic void main(String[] args){
	
		TestTask task = new TestTask();
		Thread thread = new Thread(task, "works");
		thread.start();

		sleep(5000);
		thread.interrupt();
		System.out.println("인터럽트 상태1 = ", thread.isInterrupted());
	}

	static class TestTask implements Runnable {

		@Override
		public void run(){
			while(!Thread.interrupted()){
				System.out.println("작업 중");		
			}
			try{
				Thread.sleep(1500);
			}catch (InterruptedException e){
				System.out.println("자원정리 실패");
			}
			
			System.out.println("자원 정리 및 종료");
		}
	
	}
	

}

이렇게 하면 Thread.interrupted()를 호출했을 때, 인터럽트 상태이면 true를 반환하면서
탈출하고, 인터럽트 상태는 false로 변경합ㄴ디ㅏ

따라서 이후에 자원을 정리하는 로직도 정상적으로 수행하고 Thread.sleep을 호출해도
인터럽트가 발생하지 않습니다

참고

  • 김영한의 실전 자바 - 고급 1편
profile
Software Developer

0개의 댓글