Untitled_Blue

[JAVA] 쓰레드 (Thread) 본문

Programming Language/JAVA

[JAVA] 쓰레드 (Thread)

Untitled_Blue 2023. 6. 8. 23:18
반응형

안녕하세요. 이번 글은 Thread에 대한 설명입니다.

 

- 쓰레드 (Thread)란?

  • CPU를 사용하는 최소 단위
  • 하나의 프로그램 실행 단위인 프로세스를 세분화해서 사용할 수 있는 단위
  • 단일 쓰레드 기준 각 작업을 순차적으로 처리

+ 프로그램, 프로세스, 쓰레드의 차이

  • 프로그램 (Program) : 실행 중이지 않고 설치만 되어있는 파일
  • 프로세스 (Process) : 메모리 상 로딩 중이며 실행 중인 프로그램
  • 쓰레드 (Thread) : 프로세스를 처리하기 위한 CPU를 사용하는 최소 단위

- 멀티 쓰레드

  • 하나의 프로세스 내에 2개 이상의 쓰레드가 동작하는 과정

쓰레드 하나를 일꾼이라고 생각해보자. 하나의 프로세스 안에 일꾼이 두 명 이상이 존재한다는 뜻이다. 프로세스에 대한 작업을 여럿이서 분담하기 때문에 한 명일 때보다 작업의 효율성이 증가하고 멀티 프로세스 (프로그램을 두 개 이상 실행하는 행위)보다 상대적으로 메모리 사용량이 적다. 적은 자원 사용으로 최대의 효율을 내는 효과가 있다. 또한 한 곳에서 두 개 이상의 결과를 같은 타이밍에 송출해야 하는 상황도 멀티 쓰레드를 사용한다.

 

+ 멀티 쓰레드의 조건

  • 동시성 : 매우 짧은 간격으로 교차 실행되는 방식 (동시에 실행되는 것 X, 동시 실행되는 것처럼 보이는 것 O)
  • 병렬성 : CPU 내 각각의 작업을 각각의 코어에 할당해서 동시에 실행하는 방식

- Thread 생성 및 실행

1) Thread 생성 및 실행

package classes;

class Sampling extends Thread { // 쓰레드 상속
    @Override
    public void run() {
        System.out.println("Thread Run...");
    }
}

public class Thread_Practice {
    public static void main(String[] args) {
        Sampling sm = new Sampling();

        sm.start(); // Thread Run...
    }
}

쓰레드는 기본적으로 별도의 클래스를 생성한 후 추가적으로 extends Thread를 통해 쓰레드를 상속받는 것으로부터 시작한다. 이후 메인 메서드에서 쓰레드를 상속받은 클래스에 대한 객체를 생성한 후 객체를 통해 오버라이딩한 start() 메서드를 호출해서 쓰레드를 실행한다.

2) 멀티 쓰레드 실행

- Thread 상속을 통한 멀티 쓰레드 구현

package classes;

class Sampling extends Thread { // 쓰레드 상속
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        for (int i = 0; i < 5; i++) {
            System.out.println("Second Thread : " + (i + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread_Practice {
    public static void main(String[] args) {
        Sampling sm = new Sampling();
        sm.start();

        for (int i = 0; i < 5; i++) {
            System.out.print("First Thread : " + (i + 1) + " and ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

이때 멀티 쓰레드는 서로 독립적으로 실행하기 때문에 Second Thread가 뒤에 출력되도록 하기 위해 run() 이전에 0.01초를 설정했다. 독립적이라는 점은 곧 서로가 별도의 개념이기 때문에 엇박자가 발생할 수도 있다는 뜻이다.

 

- Runnable 인터페이스 상속을 통한 구현

package classes;

class Sampling implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.print("First Thread : " + (i + 1) + " and ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread_Practice {
    public static void main(String[] args) {
        Runnable runnable = new Sampling();
        Thread thread = new Thread(runnable);

        thread.start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        for (int i = 0; i < 5; i++) {
            System.out.println("Second Thread : " + (i + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

※ 결과는 상단의 Thread를 상속한 결과와 동일하다.

상단의 소스코드는 Thread 클래스를 상속받는 대신 인터페이스 Runnable를 상속받아서 구현한 멀티 쓰레드이다.

  1. 클래스에 implements Runnable를 사용해서 run() 메서드를 오버라이딩을 받고 내부에 구현할 코드를 작성한다.
  2. 이후 메인 메서드 안에 상속받은 클래스를 사용하기 위해 Runnable를 통해 객체를 생성한다.
  3. 이때 Runnable 인터페이스 안에는 start() 메서드가 존재하지 않기 때문에 별도로 Thread에 대한 객체를 생성해주고 소괄호 안에 실행할 주체인 Runnable의 객체를 매개변수로서 넘겨줘야 한다.
  4. Thread의 객체를 사용해서 start() 메서드를 호출해서 쓰레드를 실행한다.

- 쓰레드에 관한 메서드

메서드 설명
.start() 쓰레드 시작
.getName() 쓰레드의 이름 반환
.setName(String name) 쓰레드의 이름 설정
.isAlive() 쓰레드의 실행 중에 대한 여부 반환 (Boolean 형 반환)
.run() 쓰레드 실행 시 구현한 로직 작성
.sleep(long milis) 밀리초동안 쓰레드 일시정지 (= 대기)
.getState() 쓰레드의 현재 상태 반환
.currentThread() 현재 실행 중인 쓰레드에 대한 참조값 반환
.join() 쓰레드가 종료될 때까지 대기
.getPriotity() 쓰레드에 대한 우선순위 반환
.setPriortity(int p) 쓰레드에 대한 우선순위 설정
.interrupt() 쓰레드를 안전하게 종료 (InterruptedException 예외 발생)
.interrupted() 쓰레드의 안전 종료 여부 반환 (Boolean 형 반환)

이외에도 자바 공식 API 문서에 더 많은 메서드와 생성자가 있다. 그중에서도 stop(), resume(), suspend()과 같은 문제가 많은 메서드는 Deprecated. 더 이상 사용 또는 지원되지 않는다는 점을 확인할 수 있다.

package classes;

class Sampling extends Thread {
    Thread thread = new Thread();

    @Override
    public void run() {
        int cnt = 0;

        System.out.println("Thread Name : " + thread.getName());
        System.out.println("Thread ID : " + thread.getId());
        System.out.println("Thread Priority : " + thread.getPriority());
        System.out.println("Thread isAlive : " + thread.isAlive());
        System.out.println("Thread isInterrupted : " + thread.isInterrupted());

        while (true) {
            System.out.println("Thread : " + (cnt + 1));
            cnt++;

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            if (cnt >= 10) {
                System.out.println("Thread Interrupt");
                thread.setName("This is Thread !");
                thread.interrupt();
                break;
            }
        }

        System.out.println("Thread Name : " + thread.getName());
        System.out.println("Thread isAlive : " + thread.isAlive());
        System.out.println("Thread isInterrupted : " + thread.isInterrupted());
    }
}

public class Thread_Practice {
    public static void main(String[] args) {
        Sampling sm = new Sampling();

        sm.start(); // 쓰레드 시작
    }
}

상단 소스코드를 실행한 결과 상단 이미지와 같이 쓰레드가 순차적으로 출력되다가 cnt 수가 10이 되면서부터 Interrupt()를 실행함으로써 중단되는 것을 확인할 수 있다. 또한 쓰레드의 동작여부를 시작 전과 후를 통해 정상적으로 출력됨과 쓰레드가 중단되기 직전에 setName()으로 쓰레드의 이름을 설정해줌으로써 설정 전과 후 또한 달라졌음을 확인할 수 있다.

System.out.println("Thread currentThread : " + Thread.currentThread()); 
// Thread currentThread : Thread[Thread-0,5,main]

또한 현재 실행 중인 쓰레드에 대한 참조값도 확인해볼 수 있다.

package classes;

class Sampling extends Thread {
    int mili = 0;

    Sampling (int mili) {
        this.mili = mili;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName());

            try {
                Thread.sleep(mili);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread_Practice {
    public static void main(String[] args) throws InterruptedException {
        Sampling sm = new Sampling(3000);
        Thread thread = new Thread(sm);

        thread.start();
        thread.join();

        System.out.println("쓰레드가 종료되었습니다 !");
    }
}

다음 소스코드는 .join()을 통해 쓰레드를 종료하는 과정을 보여주고 있다. 쓰레드가 시작되고 쓰레드 내 모든 과정이 끝나고 완전히 종료될 때까지 대기하다가 출력문이 나옴으로써 .join() 메서드가 쓰레드가 완전히 종료될 때까지 대기하는 점을 확인할 수 있다.

package classes;

class Sampling extends Thread {
    int mili = 0;

    Sampling (int mili) {
        this.mili = mili;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + Thread.currentThread().getState());

            try {
                Thread.sleep(mili);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread_Practice {
    public static void main(String[] args) throws InterruptedException {
        Sampling sm = new Sampling(3000);
        Thread thread = new Thread(sm);
        System.out.println(thread.getName() + " : " + thread.getState());

        thread.start();
        thread.join();

        System.out.println(thread.getName() + " : " + thread.getState());
        System.out.println("쓰레드가 종료되었습니다 !");
    }
}

상단의 소스코드를 통해 쓰레드의 현재 상태를 실행 중에 실시간으로 확인할 수 있다. 쓰레드가 생성되면 NEW, 작동 중이면 RUNNABLE, 종료되었으면 TERMINATED 상태가 나온다는 점을 알 수 있다. 이 외에도 일시정지 중이면 WAIT, BLOCKED, TIMED_WAITING의 상황별로 세 가지 중 하나가 나온다.

 

다소 부족하지만 쓰레드에 대한 기본적인 설명을 마치겠습니다.

다음 글은 JDBC에 대한 설명입니다. 감사합니다.

반응형