✅ 1. 프로세스와 스레드 개념
(1) 프로세스
- 프로그램의 통상적 실행 단위를 의미하며, 쉽게말해 프로그램 하나를 실행하면 실행 단위를 프로세스라고 부른다.
- 즉, Java도 프로그램이기 때문에 실행을 하면 하나의 프로세스로 만들어져서 실행을 하게 된다.
- 프로세스로 구분하는 이유는, 여러 프로그램을 함께 돌리기 위해 프로세스 단위로 구분을 하는 것이다.
- 프로그램이 실행이 되면 프로세서가 되는데, CPU는 여러 프로세스를 돌림 이걸 멀티테스킹이라고 함.
(2) 스레드
- 프로세스 내에 존재하는 소규모 실행 흐름을 의미한다.
- 하나의 프로세스 내에 여러 스레드가 동시에 실행이 될 수 있음.
- 하나의 프로세서안에는 여러 스레드가 만들어질 수 있으며, 프로세서가 할당받은 자원을 스레드가 공유받음.
- 자바는 하나의 프로세스가 자원을 할당받아 하나의 기본 스레드가 디폴트로 작업을 수행함.
- 쉽게말해, 기본적으로 하나의 스레드를 지원해주는데, 그것이 main() 메서드를 실행하는 메인 스레드이다.
- 즉, 단일 스레드로 기본적으로 동작을 하는 것이다. ( 추가적으로 스레드를 만들어서 멀티 스레드 프로그래밍 가능 )
- 하나의 프로세스 내에 여러 스레드가 만들어져서 동시에 실행될 수 있음, 그래서 스레드를 경량 프로세스 라고 부름
✅ 2. 멀티 스레드 개념
- 하나의 프로세스 내에서 동시 실행을 위해 존재하는 여러 스레드를 의미한다.
- Java 프로그램은 하나의 스레드인 main 스레드로 시작이 된다.
- main 스레드에서 자식 스레드를 만들고 시작을 시킬 수 있음.
- 스레드마다 여러 스레드가 동시에 돌깁적으로 실행이되고 종료가 됨.
- 쉽게말해, main 스레드인 main() 메서드 흐름에서 스레드를 만들어서 따로 동작을 하게 만들 수 있음.
✅ 3. 자바 Thread 클래스
- 자바에서는 기본적으로 java.lang 패키지의 Thread 클래스를 통해 스레드 생성관 관리를 위한 메소드를 제공해준다.
- 자바에서 독립적으로 실행되는 작업 단위인 스레드를 구현하는 방법은 해당 Thread 클래스를 생성하는 것이다.
(1) Thread 클래스 생성 방법
[1] Thread() 방식
Thread t = new Thread() {
@Override
public void run() {
System.out.println("기본 생성자로 실행");
}
};
t.start();
- 아무런 인잔도 없는 기본 생성자로, 실행할 Runnable, 스레드 이름도 정해지지 않고 만드는 방식
- 이름이 디폴트로 Thread-n 으로 들어가며, run() 메소드를 오버라이딩하거나, 하위 클래스에서 구현을 해줘야함.
[2] Thread(String name) 방식
Thread t = new Thread("MyThread") {
@Override
public void run() {
System.out.println("스레드 이름: " + getName());
}
};
t.start();
- 스레드 이름만 지정하는 생성자, run()을 오버라이딩해서 구현하거나 하위 클래스에서 구현해줘야함.
[3] Thread(Runnable target) 방식
Runnable task = () -> System.out.println("Runnable로 실행");
Thread t = new Thread(task);
t.start();
- 실행할 Runnable 객체를 넘겨서 실행할 작업을 설정하는 방식이다.
- 스레드 이름 설정은 안해서 자동으로 지정되지만, Runnable을 통해 작업을 분리할 수 있어 유연성이 높음.
- 즉, run() 메서드를 오버라이딩하거나 직접 구현할 필요가없음. 이유는 Runnable 람다로 작업을 만들어 놨기 때문
[4] Thread(Runnable target, String name) 방식
Runnable task = () -> System.out.println("Runnable + 이름으로 실행");
Thread t = new Thread(task, "Worker-1");
t.start();
System.out.println("스레드 이름: " + t.getName());
- Runnable 객체와 스레드 이름을 동시에 지정하는 방식이다.
- 작업 내용과 식별을 동시에 명확히 할 수 있어 가장 자주 쓰이는 생성자 중 하나이다.
[5] 결론
- 결론은 run() 메서드가 필수 요소이며, 스레드는 start() 메서드로 실행이 됨.
(2) Thread 클래스 주요 메소드
- void start(): run() 메서드를 호출하여 this 스레드를 실행을 시켜주는 메소드
- String getName(): this 스레드의 이름을 리턴해줌.
- void setName(String name): this 스레드의 이름을 name으로 변경
- int getPriority(): this 스레드의 우선순위를 리턴
- void setPriority(int newPriority): this 스레드의 우선순위를 newPriority로 변경
✅ 4. 멀티 스레드의 실행
- 자바 프로그램을 실행하면 디폴트로 제공해주는 스레드 1개가 있으며 해당 스레드가 main() 함수를 실행을 해줌.
- 참고로 main 스레드를 포함해 모든 스레드는 누가 언제 먼저 끝날지는 스케줄러의 의해 랜덤 처럼 종료가 됨.
- 쉽게말해, 자바 애플리케이션은 모든 사용자 스레드가 종료되어야 JVM이 완전히 종료가 된다.
- main 스레드가 종료되더라도, 다른 스레드가 살아있으면 프로그램은 계속 실행이 된다는 의미이다.
- 단, 우리가 만든 스레드가 "데몬 스레드"가 아닌 경우에 한해서 이다.
- 데몬스레드는 다른 일반 스레드가 끝나서 JVM이 종료되면 자동으로 강제 종료가 된다.
- 다만 참고 사항으로는 main 스레드의 시작으로 다른 스레드가 시작이 됨. 종료는 각각 랜덤임.
- 멀티 스레드 프로그램의 실행 결과는 예측할 수 없으며, 실행 결과가 매번 다를 수 있음.
- 각 스레드는 정해진 순서 없이 독립적으로 실행이 됨 ( 스케줄러의 의해 )
- main 스레드는 다른 스레드를 시작시키나, 다른 스레드의 실행과 무관하게 실행되고 종료가 된다.
✅ 5. 스레드의 제어
- 스레드는 스레드의 여러 메서드와 키워드를 통해 제어를 할 수 있다.
- 쉽게말해, 스레드 클래스에서 여러 메서드를 지원해주는데, 해당 메서드를 이용해서 스레드의 상태를 제어 할 수 있다.
- 즉, 스레드의 실행 상태를 변경하거나 흐름을 조절하는데 해당 메서드가 이용이 된다.
- 예를들면, start() / run() / sleep() / join() / interrupt() / yield() ... 등의 메서드들을 스레드에서 제공을 해줌.
- 해당 메서드들을 이용해서 상태를 변경 할 수 있다는 의미임.
- 또헌, 스레드가 실행 도중 잠시 멈추고 나중에 실행되더라도, CPU 레지스터와 스택 상태가 정확히 저장되고 복원되기 때문에 스레드가 중단된 부분부터 안전하게 이어서 실행이 됨.
- 즉, 공유 자원에 대한 동기화 문제만 조심하면 됨.
(1) 스레드의 상태

- 생성된 스레드가 CPU를 얻어 실행되고 최종적으로 종료될 때까지 여러 상태 변화를 겪게 됨.
- JVM이 실제로 인식하는 스레드 상태 즉, 공식적인 자바 상태는 6가지가 있음.
- CPU 코어 한개당 실행 상태에 놓여질 수 있는 스레드의 수는 오직 하나이다.
- 하나의 스레드가 실행 중(Running) 상태에서, 시분할에 의해 CPU 사용 시간이 끝나면 실행 대기 상태(Runnable)로 돌아가고, 다른 스레드가 실행 상태로 전환이 되게 된다.
- 즉, 내가 관리하기 전까지는 기본적으로 운영체제에서 스레드들을 실행 상태 <-> 실행 대기 상태로 자동으로 스케줄링하며 관리를 해주게 된다. 쉽게말해, 시분할로 번갈아가면서 상태를 변경해주면서 실행을 해준다고 보면 된다.
[1] java.lang.Thread.State Enum에 정의된 스레드 상태 6가지

- NEW: Thread 객체는 생성되었지만 아직 start()가 호출되지 않은 상태를 의미한다.
- RUNNABLE: 실행 가능한 상태(Running 또는 OS에 의해 스케줄 대기 중인 상태) - 대기 상태로 보면 됨.
- BLOCKED: 다른 스레드가 가진 모니터 락을 기다리는 중의 상태
- WAITING: 다른 스레드의 명시적인 알림이 있을 때까지 무한 대기 상태 ( wait(), join() 등 )
- TIMED_WAITING: 일정 시간 동안 대기 상태 ( sleep(), join(timeout) 등 )
- TERMINATED: 스레드가 종료된 상태 ( run() 메서드의 종류를 의미함 )
(2) 스레드의 상태 제어 메서드
[1] void setPriority(int newPriority) 메서드
- 스레드의 우선순위를 변경을 할 수 있으며, 높은 우선순위를 가지는 스레드가 CPU를 얻을 확률이 높음.
[2] static void sleep(long millis) throws InterruptedException 메서드
- 현재 실행 중인 스레드가 정해진 시간 동안 실행을 멈추고 TIMED_WAITING 상태로 들어가게 됨.
- 시간이 다 지나게 되면 RUNNABLE 상태 즉, 실행 대기 상태가 되며, 스케줄러의 의해 이후에 실행 상태가 됨.
- 다른 스레드가 sleep() 된 스레드를 interrupt() 메서드를 통해 깨우면 InterruptedException 예외 발생함.
- 자바에서는 보통 잘못된 흐름에 따른 예기치 못한 문제가 발생할 때 예외를 사용한다.
- 하지만, InterruptedExcetpion은 좀 독특하다. 이유는, 일시 정지 중인 스레드를 깨우는 행위는 예외적인 상황으로 자바에서는 보고 있으며, 스레드가 sleep() 이나, wait() 중이라는 건 이 작업을 마치길 기다린다는 자발적 결정으로 보고있으며, 외부에서 강제로 interrupt()로 깨운다는건 예외적인 부분으로 간주를 한다.
- 즉, interrupt() 메서드의 의해 깨어질 스레드는 예외인 부분으로 간주를 하기 때문에 예외처리를 해두어야함.
[3] static void yield() 메서드
public class YieldExample {
public static void main(String[] args) {
Runnable task = () -> {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println(name + " - " + i);
Thread.yield(); // 다른 스레드에게 양보
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
}
- 현재 실행중인 스레드가 잠시 실행을 멈추고 Runnable 상태 즉, 실행 대기 상태로 들어감.
- 하지만, 운영체제 스케줄러가 반드시 다른 스레드를 실행할지 보장하지 않는다.
- 즉, yield() 호출 후에도 다시 바로 같은 스레드가 실행될 수 있다는 의미이다.
- yield()는 주로 다른 스레드에게 실행 기회를 주고 싶을 때, CPU 점유 경쟁을 완화하는 용도로 쓰인다.
[4] void join() throws InterruptedException
Thread t = new Thread(() -> {
System.out.println("스레드 시작");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("스레드 종료");
});
t.start();
try {
System.out.println("main 스레드가 t 스레드를 기다립니다.");
t.join(); // t가 끝날 때까지 main 스레드 대기
System.out.println("t가 종료되어 main 스레드가 다시 실행됩니다.");
} catch (InterruptedException e) {
e.printStackTrace();
}
- 현재 실행 중인 스레드가 다른 특정 스레드가 종료될 때까지 기다리도록 하는 메서드를 의미한다.
- 스레드 두 곳에서 각자 서로 join() 메서드를 호출하게 되면 데드락(교착상태)이 발생하게 된다.
- 즉, 영원히 서로가 기다리는 상황이 되버림. 이러한 교착 상태를 고려해서 코드를 짜야한다.
[5] void interrupt() 메서드
- 스레드를 인터럽트시킴
- 스레드가 wait(), join(), sleep()에 의해 중단된 상태였다면, 그 상태에서 깨어나 Runnable 상태가 됨.
- 즉, 중단 된 상태에서 실행 대기 상태로 강제로 변경을 하게 만드는 메서드이다.
(3) 스레드 상태 제어를 위한 Object 클래스의 메소드
[1] void wait() throws InterruptedException 메서드
- 현재 실행 중인 쓰레드를 일시 정지 상태로 만드는 메서드이다.
- notify() 또는 notifyAll() 메서드가 호출이 될 때 까지 대기를 하게 된다.
- 특징으로는, synchronized 블록 안에서만 호출이 가능하다.
[2] void wait(long millis) throws InterruptedException
- vait() 메서드에 시간 제한을 준 메서드이다.
- 지정한 밀리초 만큼만 기다리고, 그 시간 동안 notify() 가 호출되지 않으면 자동으로 깨어나서 대기 상태로 들어간다.
- 즉, 타임아웃 기능이 포함된 wait() 메서드를 의미한다.
- 특징으로는, synchronized 블록 안에서만 호출이 가능하다.
[3] void notify()
- wait() 상태로 대기 중인 쓰레드들 중에서 한 개를 깨운다.
- 자바 명세서에서는 어떤 쓰레드가 깨어날지 명확하게 지정하지 않고, JVM 구현에 따라 다르다고 명시가 되어있음.
- 즉, 꺠어나는 쓰레드는 JVM 스케줄러가 결정하는데, 사실상 랜덤 혹은 임의로 한 쓰레드를 골라 깨우는 셈이다.
- 특징으로는, synchronized 블록 안에서만 호출이 가능하다.
'공부 > ☕Java' 카테고리의 다른 글
| [Java] JDBC 동작 원리와 커넥션 풀 이해하기 (2) | 2025.08.06 |
|---|---|
| [Java] 프록시 패턴, 정적 프록시, 동적 프록시: Spring 사례 중심 설명 (1) | 2025.07.09 |
| [Java] 자바 스트림(Stream) 개념 + 예제 총정리 📚 (2) | 2025.06.03 |
| [Java] 컬렉션 프레임워크 정리: List, Set, Map 인터페이스와 대표 구현체 총정리 (0) | 2025.05.30 |
| [Java] java.nio 사용법 | Path, Files, FileChannel 예제 모음 (3) | 2025.05.30 |
