경계의 경계

자바의 쓰레드 관리 본문

Java

자바의 쓰레드 관리

gigyesik 2024. 3. 2. 04:35

쓰레드 (Thread) 란 무엇인가

쓰레드는 자바 프로그램의 작업이 실행되는 경로 또는 단위이다.

모든 프로그램은 메인 쓰레드라 불리는 최소 하나의 쓰레드를 가지고 있으며, 자바 프로그램에서는 JVM 내부에 있다.

쓰레드가 여러 개이면 멀티태스킹 (Multitasking) 이 가능해진다.

멀티태스킹 (Multitasking) 의 개념

멀티태스킹은 OS 가 제공하는 유저가 동시에 여러 개의 작업을 할 수 있도록 하는 기능을 말한다.

프로세스 기반 (Process-Based) 또는 쓰레드 기반 (Thread-Based) 멀티태스킹으로 분류할 수 있다.

프로세스 기반 멀티태스킹 (Process-Based Multitasking, Multiprocessing)

멀티태스킹을 위한 각 프로세스가 다른 메모리 영역에 할당되어 있는 구조를 말한다.

메모리 영역이 다른 무거운 프로세스들이 서로 전환되므로 부하가 상대적으로 강하다.

쓰레드 기반 멀티태스킹 (Thread-Based Multitasking)

같은 메모리 영역에서 작업을 수행하므로 상대적으로 부하가 약한 멀티태스킹 구조이다.

쓰레드 의 상태 (Liftcycle)

New State

쓰레드의 기본상태. 코드가 실행되지 않고, 실행 프로레스도 기동되지 않은 상태

Active State

쓰레드의 start() 메서드의 의해 전환되는 활성 상태

  • Runnable State : 쓰레드 스케줄러에 등록되어 작업을 수행할 수 있도록 대기중인 상태
  • Running State : 쓰레드가 스케줄러에 의해 CPU 에 등록되어 작업을 수행중인 상태. 작업 종료 후 Runnable State 로 전환된다.

Waiting/Blocking State

쓰레드는 작업을 수행할 수 있지만, CPU 에서 할당한 우선순위에 밀려 작업을 기다리고 있는 상태

Timed Waiting State

쓰레드의 작업이 너무 오래 걸려서 다른 쓰레드의 작업 수행을 방해하는 경우 sleep() 메서드에 의해 일시정지된 상태

Terminated State

쓰레드의 최종상태

  • 쓰레드에 할당된 작업이 성공적으로 수행된 경우 (Normal Termination)
  • 쓰레드에 할당된 작업에 예외가 발생한 경우 (Abnormal Termination)
  • 쓰레드를 더이상 사용할 수 없게 된 경우

자바 프로그램에서 쓰레드를 사용하는 방법

Thread 클래스를 상속하거나, Runnable 인터페이스를 구현하는 방법으로 사용할 수 있다.

Thread Class 를 상속받기

public class ThreadTest1 extends Thread {
    public void run() {
        System.out.println("Thread Running");
    }

    public static void main(String[] args) {
        ThreadTest1 threadTest1 = new ThreadTest1();
        threadTest1.start(); // Thread Running
    }
}

Thread Class 를 사용하기

public class ThreadTest2 {
    public static void main(String[] args) {
        Thread t = new Thread("Thread Running");

        t.start();

        String s = t.getName();
        System.out.println(s);
    }
}

Runnable Interface 를 구현하기

public class ThreadTest3 implements Runnable{
    @Override
    public void run() {
        System.out.println("Thread Running");
    }

    public static void main(String[] args) {
        ThreadTest3 threadTest3 = new ThreadTest3();
        Thread t = new Thread(threadTest3);
        t.start(); // Thread Running
    }
}

Runnable Interface 를 사용하기

public class ThreadTest4 implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread Running");
    }

    public static void main(String[] args) {
        Runnable r = new ThreadTest4();
        Thread t = new Thread(r, "Thread");

        t.start(); 

        String s = t.getName();
        System.out.println(s); // Thread
        // Thread Running
    }
}

다중 쓰레드의 작업 간섭 예시

아래 코드의 main() 메서드를 실행하면 다음과 같은 동작이 일어난다.

  1. s1 에 클래스 할당, 내부 쓰레드 t2 NEW 상태
  2. t1 에 쓰레드 할당, t1 NEW 상태
  3. 각 쓰레드 실행, t1 t2 RUNNABLE 상태
  4. t1 sleep(), t2 실행중이므로 t1 TIMED_WAITING 상태
  5. t2 sleep() 실행, t2 TIMED_WAITING 상태
  6. t2 join() 실행, 쓰레드 종료. t2 TERMINATED 상태
  7. t1 실행 완료. t1 다시 RUNNABLE 상태
public class ThreadTest5 implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(102);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("t1 invoked By t2, t1 state : " + ThreadState.t1.getState());

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

public class ThreadState implements Runnable {
    public static Thread t1;
    public static ThreadState s1;

    @Override
    public void run() {
        ThreadTest5 tt = new ThreadTest5();
        Thread t2 = new Thread(tt);

        t2.start();
        System.out.println("t2 invoking start(), t2 state : " + t2.getState());

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

        System.out.println("t2 invoking sleep(), t2 state : " + t2.getState());

        try {
            t2.join();
            System.out.println("t2 invoking join(), t2 state : " + t2.getState());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("t1 after complete, t1 state : " + t1.getState());
    }

    public static void main(String[] args) {
        s1 = new ThreadState();
        t1 = new Thread(s1);
        System.out.println("t1 not start yet, t1 state : " + t1.getState());

        t1.start();
        System.out.println("t1 invoking start(), t1 state : " + t1.getState());

//        t1 not start yet, t1 state : NEW
//        t1 invoking start(), t1 state : RUNNABLE
//        t2 invoking start(), t2 state : RUNNABLE
//        t1 invoked By t2, t1 state : TIMED_WAITING
//        t2 invoking sleep(), t2 state : TIMED_WAITING
//        t2 invoking join(), t2 state : TERMINATED
//        t1 after complete, t1 state : RUNNABLE
    }
}

Resources

'Java' 카테고리의 다른 글

자바의 Garbage Collection  (0) 2024.03.02
자바의 JVM  (0) 2024.03.01
자바의 Stream  (1) 2024.02.29
자바의 제네릭 (Generic)  (1) 2024.02.28
자바의 네트워킹과 소켓 통신  (1) 2024.02.27