Thread 와 Runnable

자바 5 이전

Thread

스레드 생성을 위해 자바에서 미리 구현해둔 클래스이며, 유저 수준의 스레드이다.

스레드를 생성하는 방법

스레드 객체를 활용하여 멀티 스레드로 논리적 흐름을 분리하고 싶다면 Thread 상속 및 run 메서드 오버라이딩을 통해 새로운 스레드에서 코드를 실행할 수 있다.

public class ThreadExample extends Thread{

    @Override
    public void run() {
        System.out.println("스레드 예시 실행");
    }
}

...

ThreadExample thread = new ThreadExample();
thread.start();

start() 메서드를 호출 했는데 어떻게 run() 메서드가 실행 되는걸까?

단순 오버라이딩 된 메서드인 run() 을 호출하면, 메인 스레드에서 객체의 메서드를 호출하는 것 과 다름없다.

메인 스레드와 분리 되어 새로운 스레드의 공간을 할당하고, 코드를 실행하는 데에 있어 JVM 의 도움이 필요하다.

start() 메서드를 호출하면 JVM 에서 일어나는 과정

1

JNI를 통해 OS의 커널에 스레드 생성을 요청

2

스레드 생성을 위한 공간 할당

  • OS는 새로운 스레드를 위해 커널 스택과 TCB 등의 자원 할당

3

커널 스레드의 정보를 자바 스레드 객체에 업데이트

  • 커널 스레드의 스레드 ID, 상태, 이름 등

4

JVM은 해당 스레드가 자바 코드를 실행하기 위한 런타임 데이터 영역 초기화

5

OS 스케줄러 큐에 해당 스레드 배치

  • 스레드 생성이 완료되면 OS 스케줄러의 준비 큐에 배치하고, 자바 스레드 상태로는 RUNNABLE 상태로 전환

6

스케줄링 차례가 오면 CPU 자원을 할당 받아 run() 메서드 실행

주요 특징

  • 유저 스레드와 커널 스레드가 1:1 매핑되어 블락이 가능하고, 블락 시 다른 스레드를 준비 큐에서 실행 시킬 수 있다.

  • start() 명령어를 호출하면 JNI 를 통해 OS 에게 시스템 콜을 보내어 스레드를 생성하고 매핑하는 과정이 오버헤드가 발생한다.

  • run() 메서드를 오버라이드 하고, run() 메서드가 아닌 start() 메서드를 실행 해야 멀티 스레드 환경에서 코드를 실행할 수 있다.

  • Thread 는 새로운 객체의 상속 관계를 만들며, 다중 상속이 불가능한 자바에서 상속할 수 있는 부모 타입이 제한된다.

Runnable

스레드를 조작하는 run() 메서드만 제공하는 함수형 인터페이스이다. 람다식으로 구현할 수도 있고, 새로운 객체를 생성하여 구현할 수도 있다.

Runnable 인터페이스는 결과를 반환하지 않는 메서드를 제공한다. 그렇다는건 새로운 객체를 생성하여 run() 을 직접 구현하더라도, 스레드가 실행한 흐름에 대한 결과를 반환하지 못한다.

스레드를 생성하는 방법

자바에서 단순하게 스레드를 다루어야 한다면, 부모의 많은 기능을 상속 받는 Thread 보다 Runnable 을 익명 객체로 제공하든, 새로운 객체를 생성하여 구현체로 다루든 한 가지 메서드에 대한 기능만 구현하는 것이 더 효율적이다.

그렇기 때문에 대부분의 상황에서 Runnable 인터페이스를 구현하여 문제를 해결할 수 있다.

Thread 와 Runnable 의 한계

  1. run() 메서드를 void 로 제공하기 때문에 반환 값을 사용할 수 없다.

  2. start() 명령어가 실행 되면 JNI 의 OS 시스템 콜로 인해 매번 스레드를 생성하는 오버헤드가 발생한다.

Last updated