공부방
자바 스레드(Thread)? 본문
https://hyunleo.tistory.com/303
프로세스(Process) vs 스레드(Thread)를 쉽게 알아보기
프로세스?사전적 의미 : 실행 중인 프로그램독립적인 작업 단위(실행 중인 프로그램)운영체제가 관리자신 만의 메모리 공간이 따로 있다.다른 프로세스와 메모리를 공유하지 않는다.(독립적)예
hyunleo.tistory.com
오랫동안 궁금했던 프로세스와 스레드의 차이점을 정리해봤다. 그렇다면 자바 스레드란 무엇일까?
자바에서 스레드란?
- 스레드(Thread)는 프로세스 내에서 실행되는 작업 흐름
- 자바에서는 스레드를 동시에 여러 개 실행할 수 있다.(멀티스레딩)
- 하나의 프로그램(프로세스) 안에서 동시에 여러 작업을 수행할 수 있도록 해주는 기능
- 자바에서 스레드는 독립적인 실행 흐름을 가진다.
- 여러 개의 스레드를 사용하면 병렬로 작업을 처리할 수 있다.
스레드 없는 코드
public class WithoutThread {
public static void main(String[] args) {
System.out.println("작업 1 시작");
작업(); // 3초 걸리는 작업
System.out.println("작업 2 시작");
작업(); // 3초 걸리는 작업
System.out.println("모든 작업 완료");
}
public static void 작업() {
try {
Thread.sleep(3000); // 3초 동안 멈춤
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
작업 1 시작
(3초 대기)
작업 2 시작
(3초 대기)
모든 작업 완료
작업이 순차적으로 실행되기 때문에, 전체 6초가 걸린다.
스레드를 사용한다면?
class MyThread extends Thread {
public void run() {
System.out.println("스레드 시작!");
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("스레드 종료!");
}
}
public class WithThread {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 🟢 새로운 스레드 실행
System.out.println("메인 작업 실행");
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("메인 작업 종료");
}
}
메인 작업 실행
스레드 시작!
(3초 대기)
스레드 종료!
메인 작업 종료
스레드를 사용하여 여러 작업을 동시에 실행하며 실행 시간이 절반으로 줄어든다.
JVM과 스레드의 관계
- 운영체제(OS) : 원래는 운영체제(Windows, macOS, Linux)가 스레드를 관리
- 자바(JVM) : 자바에서는 운영체제 대신 JVM이 스레드를 관리
- JVM이 스레드를 몇 개 실행할지 결정
- 스레드가 어디서 실행될지 (메모리 관리) 결정
- 스레드가 언제 실행되고 언제 멈출지 결정
- 즉, 자바에서 스레드를 만들면, 운영체제가 아닌 JVM이 실행 순서를 정하고 관리해주는 것
자바에서는 프로세스가 없고 스레드만 존재한다?
- 완전히 맞는 말은 아님
- 자바 프로그램 자체는 프로세스이지만, 내부적으로 실행되는 것은 스레드이다.
- java MyApp 명령어로 자바 프로그램을 실행하면, 운영체제는 새로운 프로세스를 만든다.
- 하지만 자바 내부에서는 여러 개의 스레드가 실행되면서 작업을 수행한다.
- 즉 운영체제 관점에서는 자바 프로그램이 하나의 프로세스이다.
- 하지만 JVM 내부에서는 여러 개의 스레드가 실행되는 구조이다.
JVM이 스레드를 어떻게 관리?
- JVM이 어떤 스레드를 먼저 실행할지, 언제 실행할지 결정한다는 뜻
- JVM은 운영체제(OS)의 스레드 스케줄링 방식을 사용해서, 각 스레드의 실행 순서를 자동으로 조절
- 우선순위 기반(Priority-based) 스케줄링
- 높은 우선순위를 가진 스레드가 먼저 실행된다.
- thread.setPriority(Thread.MAX_PRIORITY); 같은 방법으로 설정 가능
- 시간 분할(time-sharing) 스케줄링
- 모든 스레드가 순서대로 짧은 시간 동안 실행된다.
- 특정 스레드가 오래 실행되지 않도록 제한
- 우선순위 기반(Priority-based) 스케줄링
- 즉, JVM은 운영체제의 스케줄링 방식을 따라 스레드를 실행 순서를 정한다.
개발자는 단순히 스레드를 생성하고 JVM에 맡긴다.
JVM이 스레드를 직접 관리하기 때문에, 개발자는 직접 컨트롤할 필요가 없다.
class MyThread extends Thread {
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 thread1 = new MyThread();
MyThread thread2 = new MyThread();
MyThread thread3 = new MyThread();
thread1.start();
thread2.start();
thread3.start();
System.out.println("메인 스레드 종료!");
}
}
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
메인 스레드 종료!
메인 스레드 종료!
Thread-1 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-1 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-0 실행 중!
Thread-2 실행 중!
Thread-2 실행 중!
실제로 필자가 실행해본 결과 thread1, thread2, thread3가 동시에 실행되지만 실행 순서는 JVM이 자동으로 결정
- new Thread()를 사용해 스레드 객체를 생성
- .start()를 호출해서 스레드를 실행
- 나머지는 JVM이 알아서 처리(스레드 실행 순서, 실행 시간 등)
더 자세한 자바 스레드의 작동 과정
위의 코드를 예로 더 자세하게 들어가보자
class MyThread extends Thread {
public void run() {
System.out.println("스레드 시작!");
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("스레드 종료!");
}
}
public class WithThread {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 🟢 새로운 스레드 실행
System.out.println("메인 작업 실행");
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("메인 작업 종료");
}
}
- 코드 분석
- 이 코드에서 2개의 스레드(메인 스레드 + 추가 스레드)가 실행된다.
- main 메서드 : 자바 프로그램이 실행될 때 가장 먼저 실행되는 "메인 스레드"
- MyThread 클래스 : Thread 클래스를 상속받아서 새로운 스레드를 만드는 역할
- 이 코드에서 2개의 스레드(메인 스레드 + 추가 스레드)가 실행된다.
- main 메서드 실행(메인 스레드)
public class WithThread {
public static void main(String[] args) {
- 자바 프로그램이 실행되면서 JVM이 main 메서드를 실행
- 즉, "메인 스레드"가 실행된다.
- 새로운 스레드 생성
MyThread thread = new MyThread();
- MyThread 객체를 생성한다. (아직 실행된 것은 아님)
- 이 단계에서는 단순히 새로운 스레드 객체를 만든 것뿐이다.
- 새로운 스레드 실행(start 호출)
thread.start();
- 새로운 스레드 실행!
- start()를 호출하면 내부적으로 run() 메서드가 실행된다.
- 하지만 바로 실행되지 않을 수도 있다 -> JVM이 스케줄링을 관리하기 때문에
- 여기서 중요한 점은 start()는 비동기적으로 실행된다는 것
- 즉, 메인 스레드는 계속 진행하고, 새로운 스레드는 JVM이 알아서 실행 시점을 정한다.
- 메인 스레드의 다음 코드 실행
System.out.println("메인 작업 실행");
- 새로운 스레드를 실행했지만, 메인 스레드는 기다리지 않고 바로 다음 코드로 진행한다..
- 즉, "메인 작업 실행"이 먼저 출력된다.
출력 결과 : 메인 작업 실행
- 메인 스레드가 3초 대기(sleep 호출)
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
- Thread.sleep(3000) -> 메인 스레드는 3초 동안 멈춤 (대기)
- 하지만 이 동안에도 새로운 스레드는 실행될 수 있음
(JVM이 새로운 스레드를 언제 실행할지 결정한다.)
- 새로운 스레드의 run() 메서드 실행
public void run() {
System.out.println("스레드 시작!");
- 새로운 스레드가 실행되면서 "스레드 시작!"이 출력된다.
출력 결과 : 스레드 시작!
- 새로운 스레드도 3초 대기 (sleep 호출)
try {
Thread.sleep(3000); // 3초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
- 새로운 스레드도 3초 동안 멈춤(대기 상태)
- 이때 메인 스레드는 별개로 실행되고 있다.
(즉, 메인 스레드와 새로운 스레드는 독립적으로 실행된다.)
- 메인 스레드의 대기 시간이 끝나면 실행 계속
System.out.println("메인 작업 종료");
- 메인 스레드는 3초 대기 후 다시 실행된다.
출력 결과 : 메인 작업 종료
- 새로운 스레드의 대기 시간이 끝나면 실행 계속
System.out.println("스레드 종료!");
- 새로운 스레드도 3초 대기 후 실행 계속된다.
출력 결과 : 스레드 종료!
실행 순서 정리
- 메인 스레드 실행 -> 메인 작업 실행 출력
- 새로운 스레드 실행 -> 스레드 시작! 출력
- 메인 스레드 3초 대기
- 새로운 스레드 3초 대기
- 메인 스레드 3초 후 실행 재개 -> 메인 작업 종료 출력
- 새로운 스레드 3초 후 실행 재개 -> 스레드 종료! 출력
실행 결과가 이렇게 나오는 이유
- start()를 호출하면 새로운 스레드가 실행되지만, 메인 스레드는 기다리지 않는다.
- Thread.sleep(3000)을 만나면, 해당 스레드는 3초 동안 멈추지만 다른 스레드는 실행 가능
- 메인 스레드와 새로운 스레드는 서로 독립적으로 실행된다.
- JVM이 스레드 실행 순서를 조절해서 실행 타이밍이 겹치거나 순서가 바뀔 수도 있다.
실행 순서가 항상 동일할까?
- JVM이 스케줄링을 관리하기 때문에 실행 순서가 달라질 수도 있다.
- 즉, "스레드 시작!"이 "메인 작업 실행"보다 먼저 나올 수도 있고,
"메인 작업 종료"가 "스레드 종료!"보다 늦게 나올 수도 있다. - JVM이 각 스레드가 언제 실행될지를 자동으로 조절하기 때문이다.
- 필자가 직접 동작했을 때 실제로 밑처럼 나온다.
메인 작업 실행
스레드 시작!
(3초 대기)
메인 작업 종료
스레드 종료!
메인 작업 실행
스레드 시작!
(3초 대기)
스레드 종료!
메인 작업 종료
실행 흐름 시각화
| 시간(초) | 메인 스레드 | 새로운 스레드 |
| 0 | 메인 작업 실행 출력 | 스레드 시작! 출력 |
| 0 ~ 3초 | 3초 대기( sleep(3000) ) | 3초 대기( sleep(3000) ) |
| 3 | 메인 작업 종료 출력 | - |
| 3 | - | 스레드 종료! 출력 |
한 줄 요약
- 메인 스레드와 새로운 스레드는 독립적으로 실행된다.
- Thread.sleep()을 만나면 해당 스레드는 대기하지만, 다른 스레드는 실행될 수 있다.
- JVM이 스레드 실행 순서를 결정하기 때문에 실행 순서는 항상 같지 않을 수도 있다.
'문법' 카테고리의 다른 글
| 쿠키 vs 세션 vs 토큰 (0) | 2025.03.27 |
|---|---|
| Java의 예외 처리 (0) | 2025.03.24 |
| JVM(Java Virtual Machine) (1) | 2025.03.24 |
| 프로세스(Process) vs 스레드(Thread)를 쉽게 알아보기 (0) | 2025.03.20 |