[Java] 프로세스와 스레드 - 4

2024. 2. 17. 13:43Memorizing/Java

728x90

본 포스팅에서는 스레드의 상태와 스레드의 상태를 제어할 수 있는 메소드인 sleep, interrupt, join, yield에 대해 다룹니다.

스레드의 상태

스레드는 new키워드를 통해서 스레드 객체를 생성하면, start 메소드를 호출하여 실행 대기 상태로 넘어갈 수 있습니다. 이 후 실행 대기 상태의 스레드 중에서 스레드 스케줄링으로 특정 스레드가 선택되면, 그 스레드의 run 메서드를 실행합니다. 그리고 스레드의 run 메소드의 작업이 모두 끝나면 그 스레드는 종료하게 됩니다. 위의 내용을 그림으로 정리하면 아래와 같습니다.

또한, 스레드의 상태는 아래와 같이 사전 정의되어 있습니다.

 

스레드의 제어

스레드의 상태를 여러가지 메소드를 호출하여 제어할 수 있습니다. 스레드의 상태를 제어하는 여러 메소드를 그림으로 정리하면 아래와 같이 정리할 수 있습니다.

sleep

sleep메소드는 스레드를 일시정지 상태로 만들어서 다음 동작이 비동기적으로 처리될 수 있도록 합니다. 예제 코드는 아래와 같습니다. 먼저 "Thread"라는 이름의 thread 객체를 만든 후, start 메소드를 호출하면 2000밀리초 동안 일시정지 하게 됩니다. 그 후, 비동기적으로 메인 스레드가 1000밀리초 동안 일시정지 한 후, "sleep(1000): main"을 출력한 후, 1초 뒤에 "Thread"스레드가 "task: Thread"를 출력합니다. 

만약 여기서 "Thread"객체의 일시정지 시간을 500 밀리초로 바꾼다면 "task: Thread"가 먼저 출력된 후, "sleep(1000): main"이 출력될 것입니다.

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("task : " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task, "Thread");
        thread.start();

        try {
            thread.sleep(1000);
            System.out.println("sleep(1000) : " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

interrupt

interrupt는 sleep 메소드를 호출하여 만든 일시정지 상태를 다시 실행대기 상태로 만드는 역할을 수행합니다. sleep은 interrupt에 의해 일시정지 상태가 풀릴 수 있기 때문에 반드시 sleep을 정의할 때는 interrupt 예외 처리를 해주어야합니다. 예제 코드는 아래와 같습니다.

아래의 코드를 실행하면, "Thread"가 실행되어 1000 밀리초 동안 일시정지하게 되는데, 그 동안 비동기적으로 interrupt 메소드가 실행되어 일시정지 상태를 실행대기 상태로 만듭니다. 이때, "Thread"가 catch문으로 예외 처리를 수행하여 "task: Thread"가 출력되고 thread의 insInterrupted 상태가 'true'로 전환됩니다. 따라서 "thread.isInterrupted() = true"가 출력됩니다.

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("task : " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task, "Thread");
        thread.start();

        thread.interrupt();

        System.out.println("thread.isInterrupted() = " + thread.isInterrupted());

    }
}

 

 

join

join 메소드는 "정해진 시간동안 지정한 스레드가 작업을 진행하지 않고 기다립니다." join 메소드에서 시간을 지정하지 않으면 다른 스레드의 작업이 끝날 때까지 기다린 후, 작업을 수행합니다. 예제 코드는 아래와 같습니다.

아래의 코드에서 "thread" thread가 시작한 후, 5초 동안 일시정지하게 되는데, 메인 스레드는 먼저 수행된 "thread"가 종료될 때까지 기다리게 됩니다. 따라서 마지막 줄에서 출력되는 것은 5000 밀리초 + 알파의 값입니다.

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                Thread.sleep(5000); // 5초
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(task, "thread");

        thread.start();

        long start = System.currentTimeMillis();

        try {
            thread.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // thread 의 소요시간인 5000ms 동안 main 쓰레드가 기다렸기 때문에 5000이상이 출력됩니다.
        System.out.println("소요시간 = " + (System.currentTimeMillis() - start));
    }
}

yield

yield 메소드는 "남은 시간을 다음 스레드에게 양보하고 스레드 자신은 실행대기 상태가 됩니다." 예제 코드는 아래와 같습니다.

아래의 코드에서는 "thread1", "thread2" 스레드가 생성된 후, start 메소드가 호출됩니다. start 메소드가 호출되면, 1000 밀리초 후 현재 스레드의 이름을 출력합니다. 이때, 비동기적으로 실행되는 메인 스레드는 5000밀리초 동안 일시정지 상태로 있다가 그 후에 "thread1"을 interrupt하여 "thread1"의 sleep 상태를 정지시키고 예외를 발생시킵니다. "thread1"에서 예외가 발생하면 yield 메소드를 호출하는데, yield 메소드가 호출되면, "thread1"이 가지고 있던 리소스를 "thread2"에게 넘겨주고 자신의 스레드는 실행대기 상태에 들어갑니다. 

아래의 코드를 실행하면 "thread1", "thread2"가 4번 출력되고 그 후에 "thread2"만 추가적으로 6번 출력됩니다. 

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                Thread.yield();
            }
        };

        Thread thread1 = new Thread(task, "thread1");
        Thread thread2 = new Thread(task, "thread2");

        thread1.start();
        thread2.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread1.interrupt();

    }
}

'Memorizing > Java' 카테고리의 다른 글

[Java] 제네릭 - 2  (0) 2024.02.19
[Java] 제네릭 - 1  (0) 2024.02.19
[Java] 프로세스와 스레드 - 3  (0) 2024.02.17
[Java] 프로세스와 스레드 - 2  (0) 2024.02.17
[Java] 프로세스와 스레드 - 1  (0) 2024.02.17