Contents
1. 프로세스(Process)1️⃣ 프로세스란?2️⃣ 프로세스가 가지는 것들3️⃣ 프로세스 vs 스레드 (핵심 비교)2. CPU의 작업 스케줄링 및 실행 방식1️⃣ 동기적 실행(Synchronous)2️⃣ 실제 컴퓨터는 동기적 실행하지 않는다!3️⃣ 컨텍스트(Context)4️⃣ 문맥 교환 (Context Switching)5️⃣ 비동기적 실행 (Asynchronous)6️⃣ CPU 관점에서의 실행7️⃣ RR(Round Robin) 스케줄링 알고리즘8️⃣ 스레드와 RR의 관계3. 스레드(Thread)1️⃣ 스레드란?2️⃣ 스레드의 특징3️⃣ 스레드의 장점4️⃣ 스레드의 단점5️⃣ 스레드를 만드는 방법*️⃣ 람다식(Lambda Expression)필기 정리⭐⭐⭐너무 너무 중요⭐⭐⭐
- 개념 제대로 이해하기!
- 문법 익히기! → Thread 를 new해서 start 하는 방법 알면 됨! (new 할 때 매개변수는 람다식으로)
1. 프로세스(Process)
1️⃣ 프로세스란?
1) 개념
실행 중인 프로그램의 인스턴스이며, 자원과 주소 공간을 독립적으로 가진 실행 단위
프로세스 = 실행 중인 프로그램 + 독립된 메모리 공간
- 프로그램(파일): 정적인 코드
- 프로세스: 메모리에 올라와 실행 중인 상태
2) 예시
java MyApp실행 → MyApp 프로세스 생성
- 브라우저 실행 → 브라우저 프로세스 생성
2️⃣ 프로세스가 가지는 것들
1) 독립된 주소 공간
- Code 영역
- Data 영역
- Heap
- Stack
➡️ 다른 프로세스와 메모리 공유 ❌
2) OS 자원
- PID (Process ID)
- 파일 디스크립터
- 권한 정보
- CPU 스케줄링 단위
3️⃣ 프로세스 vs 스레드 (핵심 비교)
구분 | 프로세스 | 스레드 |
실행 단위 | 프로그램 | 작업 흐름 |
메모리 | 완전 독립 | 공유 |
생성 비용 | 큼 | 작음 |
데이터 공유 | IPC 필요 | 변수 공유 |
안정성 | 높음 | 낮음 |
장애 영향 | 자신만 종료 | 전체 영향 가능 |
📌 자바 프로그램 = 1 프로세스 + N 스레드
2. CPU의 작업 스케줄링 및 실행 방식
1️⃣ 동기적 실행(Synchronous)
1) 정의
작업이 정해진 순서대로 실행되는 것
A 작업 끝 → B 작업 시작 → C 작업 시작2) 특징
- 순서 보장 ⭕
- 한 작업이 끝나야 다음 작업 진행
- 단순하고 이해하기 쉬움
3) 단점
- 대기 시간(wait time)이 길어질 수 있음
- 자원(CPU, I/O) 낭비 발생
2️⃣ 실제 컴퓨터는 동기적 실행하지 않는다!
1) 이유
CPU 자원을 효율적으로 쓰기 위해서
- 작업이 I/O 대기 중이면 CPU 놀게 됨
- 그래서 CPU는 여러 작업을 빠르게 오가며 처리 → 컨텍스트 필요
➡️ 겉으로 보면 동시에 실행되는 것처럼 보임 (= 동시성, Concurrency)
3️⃣ 컨텍스트(Context)
1) 개념
프로세스/스레드가 “어디까지 실행했는지”에 대한 정보
2) 포함 내용
- 프로그램 카운터
- 레지스터 값
- 스택 정보
- 상태 정보
4️⃣ 문맥 교환 (Context Switching)
1) 개념
CPU가 다른 프로세스/스레드로 전환할 때 기존 작업의 컨텍스트를 저장하고, 새 작업의 컨텍스트를 복원하는 과정
2) 과정
A 실행
→ A 컨텍스트 저장
→ B 컨텍스트 복원
→ B 실행
→ 다시A 복원3) 특징
- CPU는 실제로 하나의 작업만 실행
- 전환 비용 발생 ❗
- 너무 잦으면 성능 저하
5️⃣ 비동기적 실행 (Asynchronous)
1) 개념
작업의 시작 순서와 완료 순서가 다를 수 있는 실행 방식
2) 특징
- 누가 먼저 끝날지 모름
- 기다리지 않고 다음 작업 수행
- 결과는 콜백 / 이벤트 / Future로 처리
📌 비동기 = 무질서 ❌
👉 완료 시점을 보장하지 않는 구조 ⭕
6️⃣ CPU 관점에서의 실행
CPU 입장에서는 항상 자기 스케줄링 알고리즘 순서대로 실행
- 사람 기준: “동기 / 비동기”
- CPU 기준: 스케줄링된 순서대로만 실행
👉 비동기는 프로그래밍 모델의 개념
7️⃣ RR(Round Robin) 스케줄링 알고리즘
1) 핵심 아이디어
CPU 사용 시간을 잘게 쪼개서 공평하게 나눠 쓰자
2) 비유 (화장실)
- 화장실 1개 (CPU 1개)
- A: 10초
- B: 5초
- C: 1초
❌ 순서대로 (동기적)
A (10초)
→ B (5초)
→ C (1초)- C는 15초 기다려야 1초 사용
- 평균 대기 시간 매우 큼
⭕ RR 방식
- 화장실 사용 시간 = 1초로 제한 (Time Slice)
- A → B → C → A → B → A …
➡️ 모두 조금씩 빨리 경험
➡️ 응답성(Response Time) 개선
8️⃣ 스레드와 RR의 관계
- Time Slice 단위로 CPU를 나눠 쓰는 실행 단위 = 스레드
- 왔다 갔다 할 때마다
- 컨텍스트 저장
- 컨텍스트 복원
- 이 모든 과정 = 컨텍스트 스위칭
3. 스레드(Thread)
교재 p.687- 16 멀티 스레딩 (책 안 봐도 됨)
1️⃣ 스레드란?
프로그램 안에서 동시에 여러 작업을 수행할 수 있도록 도와주는 실행 흐름 단위
나의 프로그램 안에서 여러 일을 동시에 처리하는 방법(동시성)
- 하나의 프로세스(Process) 안에서 실행되는 독립적인 실행 흐름
- OS는 프로세스마다 하나의 main 스레드 할당
- 프로세스에서 스레드 요청해서 OS로부터 여러 개의 스레드 할당 받을 수 있음 → 추가적으로 스레드를 할당받기 위해서 스레드를 할당받을 새로운 큐가 필요함
- 자바 프로그램은 기본적으로 main 스레드 하나에서 시작
- 하지만 네트워크 작업, 파일 다운로드, 애니메이션 처리처럼 오래 걸리는 작업은 별도의 스레드로 처리하면 효율적
2️⃣ 스레드의 특징
1) 프로세스 내에서 메모리 공유
- 같은 프로세스의 스레드들은 힙(Heap) 과 정적 영역을 공유
- 스레드마다 스택(Stack) 은 별도로 가짐
2) 독립적인 실행 흐름
- 하나의 프로그램 안에서 여러 작업을 동시에 처리할 수 있음
- 예) 게임에서 “렌더링”, “물리 계산”, “사용자 입력 처리”가 각각 스레드로 동작.
3) 동시성과 병렬성 지원
- 동시성(Concurrency) : 논리적으로 동시에 수행되는 것처럼 보이는 상태
- 병렬성(Parallelism): 멀티코어 CPU에서 실제로 동시에 스레드가 수행
4) Context Switching 발생
- CPU가 여러 스레드를 번갈아 실행시키기 때문에 문맥 교환 비용이 존재
5) Thread Safety 문제
- 여러 스레드가 같은 자원에 접근하면 Race Condition 같은 문제 발생 가능
6) JVM에 의해 스케줄링
- 자바 스레드는 OS 스레드 기반이며, JVM이 OS 스케줄러 위에서 동작
3️⃣ 스레드의 장점
1) 응답성 향상
- 긴 작업을 백그라운드 스레드로 넘기고 UI는 빠르게 반응할 수 있음
- 예) 채팅앱에서 메시지 수신 스레드 + UI 스레드
2) 자원 효율성 증가
- 프로세스를 여러 개 만드는 것보다 스레드는 훨씬 가볍고 빠름
- 메모리도 공유하기 때문에 낭비가 적음
3) 멀티코어 활용 가능
- 스레드를 여러 개 사용하면 CPU 여러 코어를 동시에 사용할 수 있어 성능 향상.
4) 비동기 작업 처리에 적합
- 네트워크 I/O, 파일 처리처럼 오래 걸리는 작업을 병렬로 처리하기 좋음.
4️⃣ 스레드의 단점
1) 동기화 문제가 복잡함
- 공유 자원 접근 시 락(lock)을 사용해야 함
- 잘못하면 Deadlock, Race Condition 같은 심각한 버그 발생
2) 디버깅이 어려움
- 멀티스레드 환경의 버그는 재현하기도 어렵고 분석도 힘듦
3) 스레드 생성 비용 존재
- 스레드 생성과 문맥 교환(Context Switching)은 무시할 수 없는 오버헤드가 있음
- 그래서 스레드 풀(ExecutorService) 사용이 권장됨
4) 과도한 스레드는 성능 저하
- 스레드를 많이 만든다고 무조건 빠른 것이 아님
- CPU 코어보다 스레드가 지나치게 많으면 오히려 성능 감소
5) 자원 공유로 인한 부작용
- 모든 스레드가 힙을 공유하므로 잘못된 접근은 전체 프로세스에 영향을 줌
5️⃣ 스레드를 만드는 방법
1) Runnable 인터페이스 구현 (람다식)
예시
Thread t = new Thread(() -> {
System.out.println("람다식 스레드 실행 중...");
});
t.start();Runnable의run()메서드를 람다식으로 대체한 형태
- 가장 깔끔하고 많이 사용되는 방식
실습
package ex16;
public class Th02 {
public static void main(String[] args) {
// 스레드 생성자의 매개변수로 메서드를 전달해줘야 함(인터페이스 Runnable 타입)
Thread t1 = new Thread(() -> {
for (int i = 0; i < 200; i++) {
System.out.println("sub 스레드 : " + i);
}
});
t1.start();
for (int i = 0; i < 200; i++) {
System.out.println("main 스레드 : " + i);
}
}
}

2) Runnable 인터페이스 구현 (별도의 클래스 생성)
예시
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("Runnable 스레드 실행 중...");
}
}
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyTask());
t.start();
}
}실습
package ex16;
// 스레드는 메서드 이름 run으로만 해야하는데 다른 이름으로 할까봐 Runnable 타입 넣으라고 인터페이스 걸어둠!
class Hello implements Runnable { // 스레드 만들려면 큐 필요 -> 큐 있으려면 메서드 필요 -> 메서드 있으려면 클래스 필요
// sub 스레드
@Override
public void run() { // 새로운 스레드를 부여받을 작업 단위(큐)
for (int i = 0; i < 100; i++) {
System.out.println("sub 스레드 : " + i);
}
System.out.println("sub 스레드 종료");
}
}
public class Th01 {
// main 스레드
public static void main(String[] args) {
System.out.println("첫번째 실행");
System.out.println("두번째 실행");
System.out.println("세번째 실행");
Hello hello = new Hello(); // run 이라는 메서드(큐) 넘기려는 목적
Thread sub = new Thread(hello); // 스레드 객체 만들기(큐 만들어질려면 메서드 만들어야 함)
sub.start(); // OS에 스레드 할당받음 / run 메서드가 새로운 작업단위가 된다 -> 이제 작업단위 2개!(동시성으로 작업)
// hello.run(); 이라고 하면 동기적으로 작동해서 위의 run의 큐가 끝날 때까지 메인 큐 정지!
for (int i = 0; i < 100; i++) {
System.out.println("main 스레드 : " + i);
}
System.out.println("main 스레드 종료");
}
}


*️⃣ 람다식(Lambda Expression)
1) 람다식이란?
메서드를 값처럼 전달하는 문법메서드 1개짜리 인터페이스를 짧게 구현해서 전달하는 문법
() -> { }
2) 언제 람다식을 쓸 수 있나?
- 매개변수로 인터페이스 타입을 요구할 때
- 그 의미는 ➡️ “메서드 하나를 넘겨라”
⚠️ 단, 인터페이스에 추상 메서드가 1개일 때만 가능 (= 함수형 인터페이스)
3) 왜 1개일 때만 가능한가?
- 람다는 어떤 메서드를 구현하는지 모호하면 안 됨
- 추상 메서드가 2개 이상이면 ❌
➡️ 이 경우
- 클래스 따로 만들거나
- 익명 클래스 사용 (실무에선 거의 없음)
※ 익명 클래스
- 이름 없는 클래스
- 람다식을 못 쓸 때 대안
- 교재 p.308 참고 (필요할 때만)
※ 인터페이스의 목적
- can do
→ “이 객체는 이 기능을 할 수 있다”
- 메서드 전달
→ 구현을 외부에서 주입
- 상속처럼 타입 일치의 역할도 함 (주목적은 아님)
- 매개변수, 리턴 타입으로 사용 가능
4) 실습
package ex16;
public class Th02 {
public static void main(String[] args) {
// 스레드 생성자의 매개변수로 메서드를 전달해줘야 함(인터페이스 Runnable 타입)
Thread t1 = new Thread(() -> {
for (int i = 0; i < 200; i++) {
System.out.println("sub 스레드 : " + i);
}
});
t1.start();
for (int i = 0; i < 200; i++) {
System.out.println("main 스레드 : " + i);
}
}
}
package ex16;
public class Th02 {
public static void main(String[] args) {
// 스레드 생성자의 매개변수로 메서드를 전달해줘야 함(인터페이스 Runnable 타입)
Thread t1 = new Thread(() -> {
for (int i = 0; i < 200; i++) {
System.out.println("sub 스레드 : " + i);
}
});
t1.start();
try {
t1.join(); // t1이 끝날 때까지 메인 기다려! -> 동기적
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 200; i++) {
System.out.println("main 스레드 : " + i);
}
}
}
※ join - 새로운 스레드 작업 끝날 때까지 기다려! → 지금 안 배움!
필기 정리



Share article