초보 개발자의 성장기

genieus와 함께 알아보는 쓰레드 본문

BackEnd 지식

genieus와 함께 알아보는 쓰레드

개발자 김케빈 2022. 2. 15. 13:58

 

실무를 하면서 쓰레드가 정말 중요한 역할을 한다는 사실을 알았습니다.
그래서 이번에는 쓰레드를 쓰는 이유와 장점에 대해 포스팅하려고 합니다.
간단한 예제도 있으니 도움이 되실 거라고 생각됩니다!

쓰레드를 쓰는 이유

쓰레드는 하나의 프로세스 내부에서 독립적으로 실행되는 하나의 작업 단위를 뜻합니다. 즉, 프로세스 안에 쓰레드가 있습니다.
그래서 쓰레드를 알기 전에 프로세스에 대한 이해가 필요합니다.
프로세스란? 현재 실행중인 프로그램을 의미한다.
프로그램을 실행하면 OS로부터 실행에 필요한만큼의 자원을 할당받는데 이 때 할당받은 프로그램을 프로세스라고 칭합니다.

✻ 프로세스는 독립된 메모리 영역을 사용합니다.

 

java를 이용해 쓰레드를 사용할 때는 아래와 같은 특징을 가지고 있습니다.

1. JVM에 의해 하나의 프로세스가 발생하고 main( ) 안의 실행문들이 하나의 스레드입니다.

2. main( ) 이외의 또 다른 스레드를 만들려면 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현합니다.

3. 다중 스레드 작업 시에는 각 스레드 끼리 정보를 주고받을 수 있어 처리 과정의 오류를 줄일 수 있습니다.
    쓰레드와 달리 프로세스끼리는 정보를 주고받을 수 없습니다.

 

쓰레드를 사용하는 이유는 아래에서 확인할 수 있습니다.

  1. 메모리 절약
    OS마다 다르지만, 무슨 작업을 수행하려고 할 때 JVM은 적어도 32~64MB 물리 메모리 점유한다.
    근데 스레드는 1MB 이내의 메모리만 점유한다. 그래서 스레드를 '경량 프로세스'라고도 부른다.

  2. 프로세스 콘텍스트 스위칭(Context Switching)에 비해 오버헤드 절감
    멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행하게 되면 프로세스를 생성하여 자원을 할당하는 과정도 줄어들뿐더러 프로세스를 콘텍스트 스위칭(Context Switching)하는 것보다 오버헤드를 더 줄일 수 있게 된다.

  3. 작업들 간 통신 비용 절감
    프로세스 간의 통신 비용보다 하나의 프로세스 내에서 여러 스레드 간의 통신 비용이 훨씬 적으므로 작업들 간의 통신 부담을 줄일 수 있게 된다.

 

쓰레드 생성주기

쓰레드의 생성주기는 아래와 같습니다.

출처 : https://www.scientecheasy.com/2020/08/life-cycle-of-thread-in-java.html/

 


    • New : Thread 클래스를 사용하여 스레드 객체를 생성할 때 스레드가 생성됩니다.
      즉, 쓰레드가 생성되면 새로운 상태로 진입하지만 아직 인스턴스에서 start() 메서드가 호출되지 않은 것
      따라서, 즉, Thread 객체는 존재하지만 Thread의 실행이 아니기 때문에 어떠한 문장도 실행할 수 없다. 새 스레드에서는 start() 메서드만 호출할 수 있습니다. 그렇지 않으면 IllegalThreadStateException 이 발생합니다.
    • Runnable : 쓰레드가 실행할 준비가 되었음을 의미합니다.
      새 스레드에서 start() 메서드가 호출되면 스레드는 실행 가능한 상태가 됩니다.
      실행 가능한 상태에서 스레드는 실행할 준비가 되어 있고 프로세서의 가용성(CPU 시간)을 기다리고 있습니다. 
      즉, 스레드가 실행을 기다리고 있는 스레드의 큐(라인)에 합류했습니다.
      ※ 모든 스레드의 우선 순위가 같으면 CPU는 선착순 방식으로 스레드 실행을 위한 시간 슬롯을 할당합니다.    
          스레드에 시간을 할당하는 프로세스를 타임 슬라이싱이라고 말합니다.
    • Running : 실행은 프로세서(CPU)가 실행을 위해 스레드에 시간 슬롯을 할당했음을 의미합니다.
      스레드 스케줄러가 실행 가능한 상태에서 실행을 위해 스레드를 선택하면 실행 상태가 됩니다.
    • Blocked : 스레드가 일시 중단되거나, 휴면 중이거나, 특정 조건을 충족하기 위해 일정 시간 대기하는 경우 차단된 상태로 간주
    • Dead : 스레드는 run() 메서드가 명령문의 실행을 완료하면 자동으로 죽거나 데드 상태로 이동합니다. 
      즉, 스레드가 run() 메서드에서 나올 때 스레드가 종료되거나 죽습니다. stop() 메서드가 호출될 때 스레드도 죽을 수 있습니다.

 

쓰레드 사용 예제

쓰레드 하나만 사용해서 예제를 만들어보겠습니다.

// Runnable을 implement한 Thread
public class SingleThread_Ex1 extends Thread{

    private final int[] intArray;

    public SingleThread_Ex1(String threadname) {
        // Thread 클래스를 상속받음.
        super(threadname);
        intArray = new int[5];

        for(int start = 0; start < intArray.length;start++){
            intArray[start] = start;
        }
    }

    public void run(){
        for(int start : intArray){
            try {
                Thread.sleep(1000);

            } catch (InterruptedException ie) {
                ie.printStackTrace();
                // TODO: handle exception
            }
            System.out.println("thread Name : "+currentThread().getName());
            System.out.println("intArray value : "+start);
        }
    }

    public static void main(String[] args) {
        SingleThread_Ex1 st = new SingleThread_Ex1("SingleThread");
        st.start();
    }
}

 

또 다른 방식으로 하나의 쓰레드를 구현해보겠습니다.

public class SingleThread_Ex2 implements Runnable{

    private final int[] intArray;

    public SingleThread_Ex2(){
        intArray = new int[5];

        for(int start = 0; start < intArray.length;start++){
            intArray[start] = start;
        }
    }

    @Override
    public void run() {

        for(int start : intArray){
            try {
                Thread.sleep(1000);

            } catch (InterruptedException ie) {
                ie.printStackTrace();
                // TODO: handle exception
            }

            System.out.println("thread Name : "+Thread.currentThread().getName());
            System.out.println("intArray value : "+start);
        }
    }

    public static void main(String[] args) {

        SingleThread_Ex2 ct = new SingleThread_Ex2();
        Thread t = new Thread(ct,"SingleThread");

        t.start();
    }
}

 

이번에는 멀티 쓰레드를 구현해보겠습니다.

class Main {
    public static void main(String[] args) {
        MultiThread_Ex1 threadex = new MultiThread_Ex1();
        MultiThread_Ex1 threadex2 = new MultiThread_Ex1();
        Thread thread1 = new Thread(threadex,"A");
        Thread thread2 = new Thread(threadex2,"B");

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

        Thread.currentThread().getName();
    }
}

public class MultiThread_Ex1 implements Runnable{

    int TestNum = 0;

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            if(Thread.currentThread().getName().equals("A")){
                System.out.println("=======================");
                TestNum++;
            }
            System.out.println("ThreadName = "+Thread.currentThread().getName()+"TestNum = "+TestNum);

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

 

또 다른 멀티쓰레드 예제를 구현해보겠습니다.

class ATM implements Runnable {
    private long depositeMoney = 5000;

    public void run() {

        /* synchronized 하나가 끝나야 실행됨 */
        synchronized (this) {

            for (int i = 0; i < 10; i++) {
                notify();
                try {
                    wait();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (getDepositeMoney() <= 0)
                    break;
                withDraw(1000);
            }
        }
    }

    public void withDraw(long howMuch) {
        if (getDepositeMoney() > 0) {
            depositeMoney -= howMuch;
            System.out.print(Thread.currentThread().getName() + " - ");
            System.out.printf("잔액 : %,d 원 %n", getDepositeMoney());
        } else {
            System.out.print(Thread.currentThread().getName() + " - ");
            System.out.println("잔액이 부족합니다.");
        }
    }

    public long getDepositeMoney() {
        return depositeMoney;
    }
}

public class MultiThread_Ex2 {
    public static void main(String[] args) {
        ATM atm = new ATM();
        Thread friend = new Thread(atm, "Friend");
        Thread me = new Thread(atm, "Me");
        friend.start();
        me.start();
    }
}

Comments