minuco
article thumbnail
Published 2021. 2. 26. 23:46
스레드 (Thread) - 제어 Android/java
반응형

스레드를 생성하고 시작하려면 스레드는 다양한 상태를 가지게 됩니다. 

스레드의 상태는 자동으로 변경되거나 코드로 변경해서 제어할 수 있는데요.

이번 시간에는 스레드 제어에 대해서 알아보겠습니다.

스레드 상태

우선 스레드의 실행 흐름을 그림으로 알아보겠습니다.

스레드 객체를 생성하고 start() 메서드를 호출하면 바로 실행되는 것이 아니라 실행 대기 상태가 됩니다.

실행 대기상태란 언제든지 실행할 준비가 되어있는 상태를 말합니다.

운영체제는 실행 대기 상태에 있는 하나의 스레드를 선택하고 CPU가 run () 메서드를 실행상태로 만듭니다.

 

실행 상태의 스레드는 run() 메서드를 모두 실행하기 전에 다시 실행 대기 상태로 돌아갈 수 있고, 실행 대기 상태에 있는 다른 스레드 중에서 하나를 선택해 실행 상태가 되기도 합니다.

 

스레드는 실행 대기 상태와 실행 상태를 번갈아 가면서 자신의 run() 메서드를 조금씩 실행합니다. 실행상태에서 run() 메서드가 종료되면, 더 이상 실행할 코드가 없기 때문에 스레드의 실행이 멈추고 종료 상태가 됩니다.

 

또한, 경우에 따라서 일시 정지 상태를 가지기도 합니다. 일시정지 상태에서 바로 실행할 수는 없고 실행 대기상태로 빠져나온 후 다시 실행을 합니다. 그림으로 한번 살펴보겠습니다.

 

스레드 상태 제어

실행 중인 스레드의 상태를 변경하는 것을 스레드 상태 제어라 한다.

 

상태변화를 가져오는 메서드의 종류를 보여주는 그림입니다.

 

메소드 설명
interrupt() 일시정지상태의 스레드에서 InterruptException 을 발생시켜, 예외 처리코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 만들어요
sleep(long millis) 주어진 시간동안 스레드를 일시 정지 상태로 만들어요
stop() 스레드를 즉시 종료시켜요

 

주어신 시간 동안 일시정지

실행 중인 스레드를 일정 시간 멈추게 하고 싶다면 sleep() 메서드를 사용하면 됩니다.

try {
   Thread.sleep(1000);
 } catch(InterruptedExceotion e){
   // intrrupt() 메소드가 호출되면 실행	
 }

매개 값에는 밀리세컨즈(1/1000) 단위로 시간을 주면 됩니다. 

매개 값 1000을 주면 1초입니다.

 

일시 정지된 상태에서 주어진 시간이 되기 전에 intrrupt() 메서드가 호출되면 intrruptExceition 이 발생하기 때문에 예외처리를 해 주어야 합니다.

 

스레드의 안전한 종료

Thread를 즉시 종료하기 위해서는 stop() 메서드를 사용합니다. 이 메서드는 중요도가 떨어져 이제 잘 사용되지 않습니다. stop() 메서드로 스레드를 갑자기 종료하게 되면 스레드가 사용 중이던 자원들이 불안전한 상태로 남겨지기 때문입니다. 그렇다면 최선의 방법은???????

 

stop 플래그를 이용하는 방법

스레드는 run() 메서드가 끝나면 자동적으로 종료되므로, run() 메소드가 정상적으로 종료되도록 유도해야 해야 합니다.

코드를 보겠습니다.

 

    public class XXXThread extends Thread {
        private boolean stop; // stop 플래그 필드

        public void run() {
            while (!stop) { // stop이 true가 되면 run()이 종료된다.
                스레드가 반복 실행하는 코드;
            }
            // 스레드가 사용한 자원 정리
        }
    }

stop 필드가 false일 경우에는 while문의 조건식이 true가 되어 반복 실행하지만, stop필드가 true일 경우에는 while문의 조선식이 false가 되어 while문을 빠져나옵니다. 그리고 스레드가 사용한 자원을 정리하고, run() 메서드가 끝나게 됨으로서 스레드는 안전히 종료합니다.

무한 반복해서 출력하는 메소드

public class PrintThread extends Thread {
    private boolean stop;

    public void setStop(boolean stop){
        this.stop = stop;
    }

    public void run(){
        while (!stop){
            System.out.println("실행 중");
        }
        System.out.println("자원 정리");// stop이 true가 될때
       System.out.println("실행 종료");
    }
}

 

1초 후 출력 스레드를 중지

public class StopMain {
    public static void main(String[] args) {
        PrintThread printThread = new PrintThread();
        printThread.start();

        try { Thread.sleep(1000);

        }catch(InterruptedException e){}

        printThread.setStop(true); // 스레드를 종료하기위해 stop 필드를 true로 변경
    }
}

주목할 점은 스레드가 실행 대기 상태일 때 intrrupt() 메서드가 실행되면 즉시 IntrruptExceprion이 발생하지 않고, 스레드가 미래에 일시  정지 상태가 되면 IntrruptedException이 방생한다는 것입니다. 따라서 지금 당장 스레드가 일시 정지 상되가 되지 않으면 intrrupt() 메서드의 호출은 아무 의미가 없습니다.

 

일시 정지하지 않고도 intrrupt()의 호출 여부를 알 수 있는 방법이 있습니다. intrrupt() 메소가 호출되었다면 intrrupted()와 isInterrupted() 메서드는 true를 리턴합니다.

intrrupted()는 정적 메서드로  현재 스레드가 interrupted 되었는지 확인하는 것이고,

i sIntrrupted()는 인스턴스 메서드로 현재 스레드가 interruptedc 되었는지 확인하는 것입니다.

 

boolran status - Thread.intrrupted();
boolran status - objThread.isIntrrupted();

 

일시 정지 코드인 Thread.sleep(1)을 사용하지 않고 , Thread.intrrupted()를 사용해서 PrintThread의 intrrupt가 호출되었는지 확인한 다음 while 문을 빠져나오도록 한 것이다.

 

public class PrintThread2 extends Thread{

    @Override
    public void run() {
        while (true){
            System.out.println("실힝중");
            if(Thread.interrupted()){
                break;// while 를 빠져나엄
            }
        }

        System.out.println("자원 정리");
        System.out.println("실헹종");
    }
}

 

 

데몬 스레드

데몬 스레드는 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드입니다.

 

주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료되는데, 그 이유는 스레드의 보조역할을 수행하므로 주 스레드가 종료되면 데몬스 레드의 의미가 사라지기 때문입니다. 이 점으 제외하면 일반 스레드랑 차이가 없습니다.

 

데몬스레드의 적용 예는 워드프로세서 자동 저장, 음악 재생, 스레드 수집기 등이 잇습니다. 이 기능들은 스레드가 종료되면 같이 종료됩니다.

 

스레드를 데몬으로 만들기 위해서는 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출해 주면 됩니다.

 public static void main(String[] args) {
        Damon damon = new Damon();
        damon.setDaemon(true); // Damon를 데몬 스레드로 만듦
        damon.start();
        ...
 }

 

데몬 스레드를 적용한 예제입니다.

 

1초 주기로 save() 메서드를 호출하는 데몬 스레드

public class Damon extends Thread {
    public void save() {
        System.out.println("작업 내용을 저장함.");

    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
            save();
        }
    }
}

 

메인 스레드가 실행하는 코드

public class DemonExample {
    public static void main(String[] args) {
        Damon damon = new Damon();
        damon.setDaemon(true); // Damon를 데몬 스레드로 만듦
        damon.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }
        System.out.println("메인 스레드 종");
    }
}

오늘은 스레드의 제어와 데몬스 레드에 대해 공부해 보았습니다.

감사합니다.

 

 

출처 : 혼자 공부하는 자바, 신용권

반응형
profile

minuco

@minuco

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!