CS
[CS] 스레드(Thread), 멀티 스레드(Multi-Thread)
kimyongjun0129
2025. 4. 18. 10:01
목차
스레드(Thread)
프로그램 내에서 독립적으로 실행되는 하나의 작업 단위이다. (요리사)
싱글 스레드(Single Thread)
하나의 프로그램 내에서 하나의 스레드를 통해 하나의 작업을 처리한다. (요리사 한 명)
- 요리사가 한 명이기 때문에 여러 개의 주문이 들어온다면, 순차적으로 처리해야 한다.
멀티 스레드(Multi-Thread)
하나의 프로그램 내에서 여러 개의 스레드를 만들어 병렬 또는 동시성 있게 수행하는 기능이다. 즉, 프로세스 내에서 여러 개의 흐름이 동시에 동작하는 것이다. (요리사 여러 명)
- 요리사가 여러 명이기 때문에 여러 개의 주문이 들어온다면, 동시에 주문을 처리할 수 있다.
- 자바는 기본적으로 main 스레드 하나로 동작한다.
멀티 스레드를 사용하는 이유
- 무거운 작업 진행 시, 병렬로 처리하기 위해서이다.
- CPU 자원을 효율적으로 사용 가능
- 멀티 코어 CPU의 경우 각각의 스레드를 여러 코어에 분산시켜 병렬 처리한다.
- 프로그램은 I/O 작업(예: 파일 읽기, 네트워크 요청) 중에 대기해야 하는데, 단일 스레드는 대기 시간동안 멈춰있지만, 멀티 스레드는 대기 시간 동안 다른 스레드를 실행 가능하다.
- 큰 작업을 여러 개의 작은 작업으로 나눠서 각 스레드에 분산하면 전체 처리 속도가 증가한다.
- 여러 작업을 동시에 처리하는 것처럼 보여주기 (예: 다운로드 + 영상 재생)
❗쉽게 말해서,
- 단일 스레드는 한 명의 요리사가 하나씩 요리를 만든다.
- 멀티 스레드는 여러 명의 요리사가 동시에 다른 요리를 만든다.
- 부엌(CPU)을 놀리지 않고 계속 돌리는 것이다.
스레드 주요 개념
개념 | 설명 |
start() 메서드 | 스레드를 새로 시작한다. (⚠️ run()을 직접 호출하면 멀티스레드 아님) |
run() 메서드 | 스레드가 실행할 작업 내용 |
sleep(ms) 메서드 | 일정 시간 동안 스레드를 일시 정지 |
join() 메서드 | 다른 스레드가 끝날 때까지 기다림 |
synchronized 키워드 | 여러 스레드가 공유 자원에 동시에 접근하지 않도록 동기화 처리 |
싱글 스레드 사용 예제
더보기
public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작 :::");
String threadName = Thread.currentThread().getName(); // 현재 스레드 이름 가져오기
// ✅ 하나의 작업 단위: 숫자를 0 부터 4 까지 출력
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 0.5 초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// ✅ 추가작업
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 0.5 초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("::: 작업 끝 :::");
}
}
싱글 스레드이므로 하나의 작업이 끝나야 다음 작업을 진행할 수 있다.
멀티 스레드 사용 예제
더보기
MyThread Class
// ✅ Thread 클래스 상속으로 쓰레드 구현
public class MyThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("::: " + threadName + "쓰레드 시작 :::");
for (int i = 0; i < 5; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500); // 딜레이 0.5 초
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("::: " + threadName + "쓰레드 종료 :::");
}
}
Main
public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작");
MyThread thread0 = new MyThread();
MyThread thread1 = new MyThread();
// 1. thread0 실행
System.out.println("::: main 이 thread0 을 실행");
thread0.start();
// 2. thread1 실행
System.out.println("::: main 이 thread1 을 실행");
thread1.start();
System.out.println("::: main 쓰레드 종료");
}
}
- `Thread Class`를 상속받아 `나만의 Thread Class`를 만들 수 있다.
- 하지만 `MyThread`는 다른 Class를 더이상 상속받지 못하게 된다. (다이아 몬드 상속)
- 멀티 스레드이므로 작업을 병렬로 처리할 수 있다.
Runnable 인터페이스 활용(권장)
자바에서는 `Runnable 인터페이스`를 활용해 스레드를 구현하는 것을 권장한다.
1.유지 보수성과 재사용성 향상
- Thread 클래스는 스레드를 제어하기 위해 존재하는 클래스이다.
- Thread 클래스를 상속받아 MyThread를 구현하면 실행 로직과 스레드 제어 로직이 결합되어 한 가지 클래스에서 두 가지 역할을 담당하게 된다.
- 스레드 제어 로직 : `start()`,` join()`, `isAlive()` 등
- 실행 로직 : 숫자 0 ~ 9 출력
- 하나의 클래스는 하나의 책임만 가지는 것이 유지보수에 좋다.
- Runnable을 활용하면 실행 로직을 별도의 구현체로 분리할 수 있다.
- Thread는 쓰레드를 제어하는 역할
- Runnable 구현체는 실행 로직을 관리
더보기
MyRunnable Class
public class MyRunnable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Main
public class Main {
public static void main(String[] args) {
MyRunnable task = new MyRunnable(); // ✅ 하나의 작업 객체 선언
// ✅ 하나의 작업을 여러 쓰레드에서 공유
Thread thread0 = new Thread(task); // 작업 객체 공유
Thread thread1 = new Thread(task); // 작업 객체 공유
// 실행
thread0.start();
thread1.start();
}
}
위 Main에서 thread0의 객체를 생성할 때, MyRunnable 타입의 task 변수를 thread0의 생성자 파라미터로 넘겨주었다.
- Thread Class의 생성자를 통해, Runnable 타입의 객체를 넘겨받는다. Runnable은 MyRunnable의 부모이기 때문에, `MyRunnable`을 매개 변수로 받아 올 수 있다.
- `new Thread(task)`를 통해 Runnable 객체를 스레드에 넘겨줌으로써, Thread 객체가 실행될 때 `task.run()`이 호출되도록 설정하는 과정중 하나이다.
MyRunnable Class는 함수형 인터페이스인 Runnable의 run 메서드를 구현한다.
2. 기존 클래스 유지, 확장 가능
- Thread를 상속해서 MyThread를 구현하면, 다른 클래스를 상속받지 못한다.
- 자바는 다이아 몬드 상속으로 인해, 다중 상속을 지원하지 않는다.
- Thread를 상속하면 다른 클래스를 상속할 수 없어서 확장성이 떨어진다.
- Runnable은 인터페이스이므로 기존 클래스의 기능을 유지하면서 상속을 통해 확장 가능