Thread와 Runnable

ksh98·2024년 8월 11일

자바 멀티 쓰레딩

목록 보기
1/13

Thread 클래스

자바에서 쓰레드를 나타내는 클래스이다. Thread 클래스에의 핵심 메소드로는 다음과 같은 메소드들이 있다.

Thread 클래스의 정적 메소드들은 적용 대상이 현재 진행중인 쓰레드이다.

start

  • 쓰레드를 시작한다.
  • 단 한번만 호출될 수 있다.

동작 과정

  1. start가 호출되면 쓰레드가 실행 가능한 상태인지 즉 New 상태인지 확인한다. 그렇지 않다면 IllegalThreadStateException을 발생시킨다.

쓰레드는 New, Runnable, Waiting, Timed Waiting, Terminated 총 다섯가지의 상태를 갖는다.

  1. 쓰레드를 쓰레드 그룹에 추가한다. 쓰레드 그룹이란 서로 관련있는 쓰레드를 하나의 그룹으로 묶는 것이다.

JVM이 시작되면 system이라는 쓰레드 그룹이 생성되고 이 하위 쓰레드 그룹으로 main 쓰레드 그룹이 생성된다. 여기에는 main 메소드를 실행하는 main 쓰레드가 포함된다.

새로운 쓰레드를 생성시 쓰레들 그룹을 지정할 수 있는데 만약 지정하지 않았다면 자신을 생성하는 쓰레드가 포함된 쓰레드 그룹에 속한다.

쓰레드 그룹에 추가하며 내부적으로 필요한 작업들을 한다.

  1. 쓰레드를 JVM이 실행시킨다.

    3.1 start가 호출되면 JVM이 네이티브 메소드인 start0 메소드를 실행시킨며 쓰레드를 스케쥴러에 포함시킨다.
    3.2 쓰레드는 쓰레드 큐에서 Runnable로 대기하다 자신의 차례가 오면 run 메소드를 호출하며 동작을 수행한다.

run

  • 실제 동작하는 부분이다.
  • 생성자를 통해 run이 구현된 Runnable 객체를 넣어주거나 재정의 해야한다.
  • 쓰레드 시작을 위해선 run이 아니라 start를 호출해야 한다.
  • Thread는 자신의 run 메소드가 모두 실행되면 자동으로 종료된다.

start 호출 vs run 단독 호출

  • start를 호출하면 새로운 쓰레드를 생성하고 쓰레드가 사용할 스택을 생성한다.

  • run이 호출되면 run은 새로 생성된 쓰레드의 호출 스택에 쌓인다. 이렇게 쓰레드는 독립된 작업을 수행할 수 있다.

  • 스케쥴러에 의해 호출 스택을 번갈아 가며 수행한다.

  • 하지만 run을 단독 호출한다면 main 쓰레드에서 쓰레드를 생성한 경우 호출 스택이 생성되지 않아 단순히 main에서 메소드 호출하는 것처럼 작동한다.
    이미지 설명

sleep

  • 메소드를 멈추게 한다.
  • 자원을 놓아주지 않고 쓰레드의 진행만 잠시 멈춘 것이므로 데드락이 발생할 수 있다.

interupt

  • 다른 쓰레드를 멈추게 한다.
  • 다른 쓰레드를 멈추게 하면 멈춘 쓰레드에서 InterruptedException이 발생한다.
  • 멈춘 쓰레드는 예외를 catch해서 인터럽트 발생시 다른 작업을 할 수 있다.

join

  • 다른 쓰레드의 작업이 끝날 때까지 기다린다.
  • 쓰레드의 순서를 제어할 때 사용한다.

쓰레드 생성 방법

  • Thread 클래스를 상속받는 클래스를 만든다.
  • 생성한 클래스의 내부에서 run 메소드를 구현한다.
public static void main(String[] args){
	TestThread thread = new TestThread();
    thread.start();
}
static class TestThread extends Thread{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

Runnable 인터페이스

  • 쓰레드를 구현하기 위한 템플릿이다.
  • run 메소드 한 개만을 갖는 함수현 인터페이스이다.
    • 메소드가 한 개 뿐이라 람다로 구현할 수 있다.

쓰레드 생성 방법

  • 기본적으로 Runnable을 구현체를 Thread 클래스의 생성자에 넣으면 된다.

Runnable을 상속한 클래스 생성

  • Runnable을 상속한 클래스를 생성후 구현체를 Thread의 생성자에 넣는다.
public static void main(String[] args) throws IOException{
	TestRunnable runnable = new TestRunnable();
    Thread thread = new Thread(runnable);
    thread.start();
}
static class TestRunnable implements Runnable{
	@Override
    public void run(){
    	//작업
    }
}

익명 클래스 활용

  • 익명 클래스를 이용해 Runnable 구현체를 만들고 Thread 생성자에 넣는다.
public static void main(String[] args) throws IOException{
	Runnable runnable = new Runnable(){
    	@Override
    	public void run(){
    		//작업
    	}
    };
    Thread thread = new Thread(runnable);
    thread.start();
}

람다 활용

  • Runnable 인터페이스에 메소드가 한 개만 있으므로 람다식으로 표현할 수 있다.
public static void main(String[] args) throws IOException{
    Thread thread = new Thread(() -> System.out.println());
    thread.start();
}

Thread와 Runnable 비교

Thread

  • 별도의 클래스를 만들어야 해 번거롭다.
  • 다중 상속이 불가능 하므로 Thread 클래스를 상속받은 클래스는 다른 클래스를 상속받을 수 없다.
  • 더 많은 자원을 필요로 한다.
  • 람다를 사용할 수 없다.

Runnable

  • 인터페이스이기 때문에 Runnable을 상속하는 동시에 다른 클래스를 상속받을 수 있다.
  • 자원 사용량이 적다.
  • 람다를 사용할 수 있다.

이러한 이유로 Runnable을 사용하는 것이 권장된다.

공통적인 한계

하지만 Thread와 Runnable을 모두

  • 너무 저수준의 api라 프로그래밍이 어렵다.
  • 쓰레드의 작업이 끝난 후 결과 값을 반환하는 것이 불가능하다.
    • run은 void 타입이기 때문이다.
  • 새로운 쓰레드를 생성하고 종료하는데 오버헤드가 발생한다.
  • 생성된 쓰레드들의 관리가 어렵다.

이러한 이유로 자바에서 쓰레드를 쉽게 이용하기 여러 api들이 나왔다.

데몬 쓰레드

쓰레드는 크게 사용자 쓰레드와 데몬 쓰레드 두가지로 구분할 수 있다.

  • 사용자 쓰레드
    • 프로그램의 주요 작업을 수행한다.
    • 작업이 완료될 때까지 실행된다.
    • 모든 사용자 쓰레드가 종료된면 JVM도 종료된다.

데몬 쓰레드는 다음과 같은 특징을 갖는다.

  • 백그라운드에서 보조적인 작업을 수행한다.
  • JVM은 데몬 쓰레드가 종료되는 것을 기다리지 않는다. 모든 유저 쓰레드가 종료되면 데몬 쓰레드는 자동으로 종료된다.
  • setDaemon(true)를 호출하여 데몬 쓰레드로 지정할 수 있다.
    • 단 setDaemon은 start가 호출되기 전에 호출해야 하며 그렇지 않으면 IllegalThreadStateException이 발생한다.
profile

0개의 댓글