스레드 생성과 실행 방식

송현진·2025년 5월 23일
0

CS공부

목록 보기
7/17

자바에서 스레드를 생성하는 방법은 두 가지가 있다.

  • Thread 클래스를 상속하는 방식
  • Runnable 인터페이스를 구현하는 방식

Thread 클래스 상속

자바는 예외도 스레드도 객체로 다룬다. 직접 스레드를 만들기 위해 Thread 클래스를 상속해서 run() 메서드를 구현할 수 있다.

public class HelloThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": run()");
    }
}
  • Thread 클래스를 상속하고, 스레드가 실행할 코드를 run() 메서드에 재정의한다.
  • Thread.currentThread() 를 호출하면 해당 코드를 실행하는 스레드 객체를 조회할 수 있다.
  • Thread.currentThread().getName() : 실행 중인 스레드의 이름을 조회한다.
public class HelloThreadMain {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + ": main() start");
        HelloThread helloThread = new HelloThread();
        System.out.println(Thread.currentThread().getName() + ": start() 호출 전");
        helloThread.start(); // 스레드 실행 시작
        System.out.println(Thread.currentThread().getName() + ": start() 호출 후");
        System.out.println(Thread.currentThread().getName() + ": main() end");
    }
}

앞서 만든 HelloThread 스레드 객체를 생성하고 start() 메서드를 호출한다. start() 메서드는 스레드를 실행하는 아주 특별한 메서드이다. start() 를 호출하면 HelloThread 스레드가 run() 메서드를 실행한다.

⚠️ run() 메서드가 아니라 반드시 start() 메서드를 호출해야 한다. 그래야 별도의 스레드에서 run() 코드가 실행된다.

start() vs run() 차이
run()을 직접 호출하면 메인 스레드에서 동작함
start()를 호출해야 별도 스레드가 생성되어 비동기 실행됨

main: main() start
main: start() 호출 전
main: start() 호출 후
Thread-0: run()
main: main() end

스레드 간 실행 순서는 보장하지 않는다.

스레드 간의 실행 순서는 얼마든지 달라질 수 있다. CPU 코어가 2개여서 물리적으로 정말 동시에 실행될 수도 있고 하나의 CPU 코어에 시간을 나누어 실행될 수도 있다. 그리고 한 스레드가 얼마나 오랜기간 실행되는지도 보장하지 않는다. 한 스레드가 먼저 다 수행된 다음에 다른 스레드가 수행될 수도 있고 둘이 완전히 번갈아 가면서 수행되는 경우도 있다. 스레드는 순서와 실행 기간을 모두 보장하지 않는다. 이것이 바로 멀티스레드다.

실행 결과를 보면 main() 메서드는 main 이라는 이름의 스레드가 실행하는 것을 확인할 수 있다. 프로세스가 작동하려면 스레드가 최소한 하나는 있어야 한다. 그래야 코드를 실행할 수 있다. 자바는 실행 시점에 main 이라는 이름의 스레드를 만들고 프로그램의 시작점인 main() 메서드를 실행한다.

  • HelloThread 스레드 객체를 생성한 다음에 start() 메서드를 호출하면 자바는 스레드를 위한 별도의 스택공간을 할당한다.
  • 스레드 객체를 생성하고 반드시 start() 를 호출해야 스택 공간을 할당 받고 스레드가 작동한다.
  • 스레드에 이름을 주지 않으면 자바는 스레드에 Thread-0, Thread-1과 같은 임의의 이름을 부여한다.
  • 새로운 Thread-0 스레드가 사용할 전용 스택 공간이 마련되었다.
  • Thread-0 스레드는 run() 메서드의 스택 프레임을 스택에 올리면서 run() 메서드를 시작한다.

메서드를 실행하면 스택 위에 스택 프레임이 쌓인다

main 스레드는 main() 메서드의 스택 프레임을 스택에 올리면서 시작하고 직접 만드는 스레드는 run() 메서드의 스택 프레임을 스택에 올리면서 시작한다.
실행 결과를 보면 Thread-0 스레드가 run() 메서드를 실행한 것을 확인할 수 있다.

  • main 스레드가 HelloThread 인스턴스를 생성한다. 이때 스레드에 이름을 부여하지 않으면 자바가 Thread-0 , Thread-1 과 같은 임의의 이름을 부여한다.
  • start() 메서드를 호출하면 Thread-0 스레드가 시작되면서 Thread-0 스레드가 run() 메서드를 호출한다.
  • 여기서 핵심은 main 스레드가 run() 메서드를 실행하는게 아니라 Thread-0 스레드가 run() 메서드를 실행한다는 점이다.
  • main 스레드는 단지 start() 메서드를 통해 Thread-0 스레드에게 실행을 지시할 뿐이다. main 스레드가 run() 을 호출하는 것이 아니다. main 스레드는 다른 스레드에게 일을 시작하라고 지시만 하고 바로 start() 메서드를 빠져나온다.
  • 이제 main 스레드와 Thread-0 스레드는 동시에 실행된다.
  • main 스레드 입장에서 보면 그림의 1, 2, 3번 코드를 멈추지 않고 계속 수행한다. 그리고 run() 메서드는 main 이 아닌 별도의 스레드에서 실행된다.

run() 직접 호출한 경우

public class BadThreadMain {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + ": main() start");
        BadThreadMain badThread = new BadThreadMain();
        System.out.println(Thread.currentThread().getName() + ": run() 호출 전");
        helloThread.run(); // run() 직접 실행
        System.out.println(Thread.currentThread().getName() + ": run() 호출 후");
        System.out.println(Thread.currentThread().getName() + ": main() end");
    }
}

helloThread.run() : start() 를 호출해야 하지만 여기서는 문제를 확인하기 위해 run() 을 직접 호출한다.

main: main() start
main: run() 호출 전
main: run()
main: run() 호출 후
main: main() end

  • 실행 결과를 잘 보면 별도의 스레드가 run() 을 실행하는 것이 아니라 main 스레드가 run() 메서드를 호출할 것을 확인할 수 있다.
  • 자바를 처음 실행하면 main 스레드가 main() 메서드를 호출하면서 시작한다.
  • main 스레드는 HelloThread 인스턴스에 있는 run() 이라는 메서드를 호출한다.
  • main 스레드가 run() 메서드를 실행했기 때문에 main 스레드가 사용하는 스택위에 run() 스택 프레임이올라간다.

결과적으로 main 스레드에서 모든 것을 처리한 것이 된다. 스레드의 start() 메서드는 스레드에 스택 공간을 할당하면서 스레드를 시작하는 아주 특별한 메서드이다. 그리고 해당 스레드에서 run() 메서드를 실행한다. 따라서 main 스레드가 아닌 별도의 스레드에서 재정의한 run() 메서드를 실행하려면 반드시 start() 메서드를 호출해야 한다.

장점

  • 간단한 구현: Thread 클래스를 상속받아 run() 메서드만 재정의하면 된다.

단점

  • 상속의 제한: 자바는 단일 상속만을 허용하므로 이미 다른 클래스를 상속받고 있는 경우 Thread 클래스를 상속받을 수 없다.
  • 유연성 부족: 인터페이스를 사용하는 방법에 비해 유연성이 떨어진다.

Runnable 인터페이스 구현

Runnable 인터페이스

package java.lang;

public interface Runnable {
	void run();
}

스레드 실행용 인터페이즈

public class HelloRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": run()");
    }
}

실행 메인

public class HelloRunnableMain {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ": main() start");
        HelloRunnable runnable = new HelloRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        System.out.println(Thread.currentThread().getName() + ": main() end");        
        
    }
}

실행 결과

main: main() start
main: main() end
Thread-0: run()

실행 결과는 기존과 같다. 차이가 있다면 스레드와 해당 스레드가 실행할 작업이 서로 분리되어 있다는 점이다. 스레드 객체를 생성할 때, 실행할 작업을 생성자로 전달하면 된다.

장점

  • 상속의 자유로움: Runnable 인터페이스 방식은 다른 클래스를 상속받아도 문제없이 구현할 수 있다.
  • 코드의 분리: 스레드와 실행할 작업을 분리하여 코드의 가독성을 높일 수 있다.
  • 여러 스레드가 동일한 Runnable 객체를 공유할 수 있어 자원 관리를 효율적으로 할 수 있다.

단점

  • 코드가 약간 복잡해질 수 있다. Runnable 객체를 생성하고 이를 Thread 에 전달하는 과정이 추가된다.

Thread vs Runnable

구분Thread 상속Runnable 구현
상속 제약있음 (단일 상속)없음 (다중 구현 가능)
코드 분리없음실행 코드와 스레드 분리 가능
유연성낮음높음
재사용성낮음높음 (동일 Runnable 여러 스레드 공유 가능)

결론적으로 스레드를 사용할 때 Thread를 상속 받는 방법 보다 Runnable 인터페이스를 구현하는 방식을 사용하자. Runnable 방식이 더 유연하고 유지보수 하기 쉬운 코드를 만들 수 있다.

📝 배운점

이번 학습을 통해 자바에서 스레드를 생성하고 실행하는 두 가지 방법인 Thread 클래스 상속 방식과 Runnable 인터페이스 구현 방식의 구조와 차이를 명확히 이해할 수 있었다. 특히 start()run()의 차이를 직접 코드로 확인하면서 스레드의 실행 흐름과 독립적인 스택 공간 할당 등 내부 메커니즘에 대한 개념이 훨씬 명확해졌다. 단순히 문법을 아는 수준을 넘어서 실제 스레드가 생성되고 실행되는 과정에서 어떤 일이 일어나는지를 스택 프레임과 실행 순서를 중심으로 시각적으로 파악할 수 있었던 점이 가장 인상 깊었다.

또한 스레드를 설계할 때 Runnable 방식을 사용하는 것이 상속 제약을 피하고 실행 로직과 스레드 자체를 분리함으로써 코드의 유연성과 재사용성을 높인다는 점도 중요하게 느껴졌다. 다수의 작업을 병렬로 처리하거나 동일한 로직을 여러 스레드에서 공유하는 경우에는 Runnable 방식이 확실히 더 적합하다는 걸 체감했다. 앞으로 멀티스레드 프로그래밍을 구현할 때 단순히 동작하는 코드가 아닌 구조적으로 유지보수에 강하고 효율적인 코드 작성을 위해 어떤 방법을 선택할지 고민하는 습관을 들일 수 있을 것 같다.

참고

profile
개발자가 되고 싶은 취준생

0개의 댓글