컴퓨터의 CPU 코어는 여러 개인데, 프로그램이 스레드를 하나만 쓴다면(싱글 스레드) 나머지 코어들은 놀게되는데, 이 코어들에게 명령을 내려 전체의 역할을 세부적으로 할당해 하나의 방향성으로 나아가게 하는 것이 Java Thread 라고 할 수 있다. 관련 포스팅은 바로 다음에 쓸 것인데, 일단 Java Thread 가 사용되는 시기를 말하면
- 웹 서버 (Spring/Tomcat): 수천 명의 사용자가 동시에 접속할 때, 사용자 한 명당 스레드 하나를 배정하여 기다리지 않게 처리합니다.
- UI 프로그램 (안드로이드/데스크톱 앱): 파일을 다운로드하거나 무거운 계산을 할 때 '작업 스레드'를 따로 돌려야 화면이 멈추지(프리징) 않습니다.
- 게임: 그래픽 렌더링 스레드, 물리 엔진 스레드, 네트워크 통신 스레드를 분리하여 끊김 없는 게임 플레이를 제공한다
사실 이렇게 말해도 머릿속에 확! 하고 그려지지 않는데, 우리 또 백병원에 가서 공부해보자

제1막: 윽, 여기가 어디요? (스레드 생성과 실행)
[장면: 백병원 301호실] 심영이 가쁜 숨을 몰아쉬며 메인 스레드에서 깨어난다
심영: (눈을 뜨며 괴로워한다) 으윽... 아... 아아... 여기가... 어디요?
의사 양반: 아, 병원이오. 안심하세요. 지독한 멀티스레드 충돌을 겪었지만, 일단 start()는 성공했소.
심영: ...병원이요?'
의사 양반: 지독한 교착 상태(Deadlock)였소. 하마터면 프로그램이 뻗을 뻔했단 말이오.
💻 코드 고증: 스레드를 만드는 두 가지 운명
// 방법 1: 심영 클래스 (Thread 상속)
class SimYoung extends Thread {
public void run() {
System.out.println("심영: 여기가... 어디요...?");
}
}
// 방법 2: 의사 양반 클래스 (Runnable 구현)
class DoctorTask implements Runnable {
@Override
public void run() {
System.out.println("의사 양반: 안심하세요, 병원이오.");
}
}
public class Main {
public static void main(String[] args) {
SimYoung sim = new SimYoung();
Thread doc = new Thread(new DoctorTask());
sim.start(); // 심영 실행!
doc.start(); // 의사 양반 실행!
}
}
제2막: 청천벽력 (스레드 상태와 절규)
심영: ...의사 양반, 그럼 나는... 이제 실행될 수 있는 거요?
의사 양반: (단호하게) 아니, 선생은 앞으로... 자원을 가질 수가 없습니다. 다시 말해서, 싱글 스레드로 전락했다 그 말입니다.
심영: (경악하며) 뭐라고?! 그게 무슨 소리요?! 내가... 내가 싱글 스레드라니! 내가 고자... 아니, 내가 싱글 스레드라니!!
간호사: 환자분, 안정을 취하세요. 지금은 TIMED_WAITING 상태입니다!
코드 고증: 스레드의 상태 변화 (Thread.State)
- NEW: 생성만 된 상태 (심영이 병원에 오기 전)
- RUNNABLE: 실행 중 (심영이 소리 지르는 중)
- TIMED_WAITING: sleep() 중 (의사 양반이 안정을 취하라고 한 상태)
- TERMINATED: 실행 종료 (심영의 인생... 아니 프로그램 종료)
try {
System.out.println("심영: 아핡핡핡! 내가 싱글 스레드라니!!");
Thread.sleep(3000); // 3초간 TIMED_WAITING (안정)
} catch (InterruptedException e) {
System.out.println("심영: 누가 내 잠(sleep)을 깨우는 거요!");
}
제3막: 전화기 쟁탈전 (동기화와 임계 영역)
심영: 전화... 전화 좀 갖다 주시오! 어머니께 알려야 하오!
의사 양반: 안 됩니다. 지금 전화기는 공유 자원이되어 있소.
코드 고증: synchronized (임계 영역)
여러 스레드가 동시에 전화기를 쓰려고 하면 데이터가 꼬입니다.
한 명씩만 쓰게 잠궈야 합니다.
class Telephone {
// synchronized: 상두가 전화를 다 쓸 때까지 심영은 대기(Blocked)해야 함
public synchronized void use(String name) {
System.out.println(name + "이(가) 수화기를 들었습니다.");
try { Thread.sleep(2000); } catch (Exception e) {}
System.out.println(name + "이(가) 전화를 끊었습니다.");
}
}
// 상두 스레드와 심영 스레드가 이 객체를 공유하면, 한 명씩만 통화 가능!
제4막: 백병원의 질서 (스레드 풀)
코드 고증: ExecutorService
매번 스레드를 만들면 메모리가 부족해집니다. 미리 만들어놓고 재사용합시다.
import java.util.concurrent.*;
// 의사 양반 3명으로 구성된 팀
ExecutorService doctorPool = Executors.newFixedThreadPool(3);
// 10명의 환자(Task)가 몰려와도 3명의 의사가 차례대로 처리
for (int i = 1; i <= 10; i++) {
int patient = i;
doctorPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 의사가 " + patient + "번 환자를 진료 중...");
});
}
에필로그: "공산당 할 거야, 안 할 거야!"
김두한: (갑자기 나타나 권총을 들이대며) 심영이! 너, 멀티스레드 제대로 할 거야, 안 할 거야!
심영: (겁에 질려) 안 하겠소! 동기화 오류 안 내겠소! 다신 데드락 안 걸겠소!
- volatile: 변수를 메인 메모리에 저장해 스레드 간 가시성을 확보하십시오. 안 그러면 김두한이 화를 낼 겁니다.
- wait() / notify(): 스레드끼리 서로 신호를 주고받으며 협력하십시오. 의사 양반과 심영처럼 말이죠.
- Deadlock: 서로 자원을 붙잡고 안 놔주면 백병원은 영원히 멈춥니다.
의사 양반: 선생은 운이 좋았소. 다음엔 Exception이 아니라 Error가 날 것이오.
심영: "아이고... 난 이제 죽었어... (프로그램 종료)"
다음 포스트에 좀 더 Thread 공부해보자
'컴퓨터 공학 > Java' 카테고리의 다른 글
| [JAVA] 내가 쌍둥이를 낳았어 근데 첫째 둘째 생긴거 같으면 동일인물임? (0) | 2026.02.07 |
|---|---|
| [JAVA] 나 자바 개고수인데 자바 장점 알려준다 (1) | 2026.02.06 |
| [Java] JVM을 좀 배워야지 C++ 같은거 안하지 (0) | 2026.02.05 |
| [Java] 으흐흐..... HashMap 쓸거야 ! HashSet 쓸거야? (0) | 2026.02.04 |
| [Java] 그래서 Iterator 는 왜 쓰는거임? 걍 for 쓰지;? (0) | 2026.02.04 |