스레드란?
- 프로세스 내에서 실행되는 여러 개의 실행 단위로, 경량화 프로세스라고 부르기도 한다.
- 프로세스 내의 주소 공간이나 자원을 공유할 수 있다.
- 각각 스레드는 각자의 스택과 레지스터 셋을 가지고 있다.
스레드 제어 블록 (Thread Control Block, TCB)
- 스레드를 관리하기 위해 사용하는 데이터 구조
- 스레드 ID (Thread ID)
- 스레드 상태 (Thread State)
- 프로그램 카운터 (Program Counter, PC)
- 레지스터 값 ( Registers)
- 스택 포인터 (Stack Pointer)
- 우선순위 (Priority)
- 스레드별 리소스 정보
- 스레드 소속 정보
- 동기화 정보
Thread 생성
자바에서 멀티 스레드 프로그래밍을 구현하는 방법에는 크게 두 가지가 있다.
1. Thread 클래스 상속받아 구현
2. Runnable 인터페이스 구현하는 방법
Thread 클래스를 상속받는 방법
Thread 클래스를 상속받아 run() 메서드를 오버라이딩하고, 객체를 생성하여 start() 메서드를 호출합니다.
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 실행 중");
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
장점
1. 구현이 간단함
- Thread 클래스를 직접 상속받아 사용 가능하므로 코드가 직관적이고, 별도의 객체를 생성하지 않아도 됌.
2. 추가적인 기능 제공
- Thread 클래스에는 start(), sleep(), join() 등의 메서드가 포함되어 있어 스레드의 실행을 더욱 쉽게 관리
3. 상속을 활용한 기능 확장
- Thread 클래스를 상속받으면 필요한 경우 추가적인 메서드를 정의하고 기능 확장 가능
단점
1. 다중 상속이 불가능함
2. 객체를 재사용할 수 없음
- Thread 객체는 한 번 실행된 후 다시 실행할 수 없음. 새로운 스레드를 실행하려면 새 객체를 생성해야 한다.
3. 재사용성이 낮음
- 다른 클래스에서 동일한 스레드 작업을 실행하려면 새 클래스를 만들어야 하므로, 코드 재사용성이 떨어짐.
Runnable 인터페이스를 구현하는 방법
Runnable 인터페이스를 구현한 후, Thread 객체를 생성할 때 Runnable 구현체를 인자로 전달하여 실행합니다.
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 실행 중");
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
장점
1. 다중 상속이 가능함
- Runnable 인터페이스는 단순히 인터페이스이므로, 다른 클래스를 상속받으면서도 멀티스레드를 구현할 수 있습니다.
class MyClass extends SuperClass implements Runnable {
@Override
public void run() {
System.out.println("Runnable 스레드 실행");
}
}
2. 객체 재사용성이 높음
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
- 같은 Runnable 객체를 여러 개의 Thread에서 실행할 수 있어 메모리 사용이 효율적이다.
3. 코드 분리가 용이함
- Runnable을 사용하면 스레드 실행 코드와 스레드 제어 (Thread 클래스) 코드가 분리되어 유지보수와 확장이 쉬움
4. Thread 풀에서 사용 가능
- Runnable은 ThreadPoolExecutor와 같은 스레드 풀(Thread Pool)에서 실행할 수 있습니다.
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(new MyRunnable());
executor.shutdown();
단점
1. 스레드 제어 기능 부족
- Runnable은 run() 메서드만 제공하므로, Thread 클래스의 메서드(start(), join(), interrupt(), setPriority())를 직접 사용할 수 없다. 스레드를 제어하려면 Thread 객체를 별도로 생성해야 하며, 실행 제어가 다소 번거로워진다.
2. Thread 클래스의 메서드를 오버라이딩할 수 없음
3. 스레드와 실행 로직이 분리됨.
- Runnable 객체는 실행할 로직만 포함하며, 스레드 실행을 위해 new Thread(runnable).start()와 같은 방식으로 별도의 Thread 객체가 필요합니다.
4. 스레드 객체 공유 가능으로 인한 동기화 문제 발생 가능
- Runnable 객체는 여러 Thread 객체에서 공유될 수 있어, 여러 스레드가 동시에 run()을 실행할 경우 동기화 문제가 발생할 수 있습니다.
Thread vs Runnable
Thread 클래스와 Runnable 인터페이스의 관계
- Runnable 인터페이스는 인스턴스 메서드를 하나만 가지고 있는 Functional 인터페이스입니다.
- Thread 클래스도 Runnable 인터페이스의 구현 중 하나입니다.
생각해 보기
Thread 클래스는 어떠한 경우에 유리할까요?
- 스레드의 동작을 확장하거나 수정해야 하는 경우
- 스레드 자체의 속성이나 메서드를 직접 제어해야 할 때
- 단일 상속만 필요한 간단한 스레드 구현시
- 스레드의 생명주기를 명확하게 제어해야 하는 경우
Runnable 인터페이스로 구현할 경우, 객체의 재활용성을 높일 수 있습니다. 이때 주의해야 할 점은?
- 스레드 안전성(Thread-safety) 보장 필요
- 공유 자원에 대한 동기화 처리
- run() 메서드 내에서의 예외 처리
- 스레드 종료 조건 명확히 정의
- 메모리 누수 방지를 위한 자원 해제
Runnable 인터페이스로 스레드 구현시 실행 코드와 제어 코드가 분리되는 문제가 발생합니다. 어떠한 관리 방법으로 보완할 수 있을까요?
'🚣활동 > NHN Academy' 카테고리의 다른 글
Thread & Concurrency (1) | 2025.02.04 |
---|---|
Main Thread, Single Thread, Multi Thread, Daemon Thread (1) | 2025.02.03 |
Process (프로세스) (0) | 2025.02.03 |
Stream (1) | 2025.01.24 |
Lambda Expression (0) | 2025.01.20 |