4주차 Unit 1.1 — 멀티태스킹 vs 멀티프로세싱

Psj·2026년 5월 20일

F-lab

목록 보기
120/240

Unit 1.1 — 멀티태스킹 vs 멀티프로세싱

F-LAB JAVA · 4주차 · Phase 1 · 동시성의 기초
🚀 4주차 시작 — 동시성의 세계 진입


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • 멀티태스킹 (Multitasking) 의 정의와 메커니즘은?
  • 멀티프로세싱 (Multiprocessing) 의 정의와 메커니즘은?
  • 시분할 (Time Sharing) 이 "동시 실행처럼" 보이게 하는 원리는?
  • 스케줄링 (Scheduling) 의 역할은?
  • 단일 코어 에서도 동시 실행이 가능해 보이는 이유는?
  • 멀티태스킹과 멀티프로세싱 을 함께 사용 가능한가?
  • 동시성 (Concurrency) vs 병렬성 (Parallelism) 의 차이는?
  • 컨텍스트 스위칭 의 비용은?
  • 실무에서의 의미 는?

🎯 핵심 한 문장

멀티태스킹 (Multitasking) 은 1개의 CPU 코어가 여러 작업을 번갈아 가며 (시분할) 실행하여 "동시 실행처럼" 보이게 하는 소프트웨어 기법이고, 멀티프로세싱 (Multiprocessing) 은 여러 CPU 코어가 실제로 동시에 작업을 실행하는 하드웨어 기반 방식이다.
시분할 (Time Sharing) 은 CPU 시간을 매우 잘게 쪼개 여러 프로그램에 빠르게 번갈아 할당하므로, 사람 눈에는 동시 실행으로 보인다 — 이를 동시성 (Concurrency) 이라 한다.
반면 멀티프로세싱은 물리적으로 여러 코어가 동시 수행하므로 병렬성 (Parallelism) 이다.
둘은 함께 사용 가능 — 현대 시스템은 멀티코어 (멀티프로세싱) 각각에서 멀티태스킹을 한다.
작업 전환 시 컨텍스트 스위칭 비용이 발생하므로, 무한정 작업을 늘리면 오히려 느려진다.

비유 — 요리사와 주방

멀티태스킹 (1명의 요리사):
  요리사 1명이 여러 요리를 번갈아
  - 파스타 면 삶다가 → 소스 젓다가 → 다시 면 보다가
  - 빠르게 전환하니 "동시에 하는 것처럼"
  - 실제로는 한 순간에 1가지만
  - 동시성 (Concurrency)

멀티프로세싱 (여러 요리사):
  요리사 4명이 각자 다른 요리
  - 요리사 A: 파스타
  - 요리사 B: 스테이크
  - 요리사 C: 샐러드
  - 진짜 동시에 4가지
  - 병렬성 (Parallelism)

함께 사용 (4명 + 각자 멀티태스킹):
  요리사 4명, 각자 여러 요리 번갈아
  - 현대 시스템의 모습
  - 멀티코어 + 시분할

→ 멀티태스킹 = 번갈아 (동시성), 멀티프로세싱 = 진짜 동시 (병렬성).


🧭 9개 섹션 로드맵

1. 멀티태스킹의 정의와 메커니즘
2. 멀티프로세싱의 정의와 메커니즘
3. 시분할 (Time Sharing) 의 원리
4. 스케줄링의 역할
5. 동시성 vs 병렬성
6. 단일 코어에서의 "동시 실행"
7. 멀티태스킹 + 멀티프로세싱 함께
8. 실무에서의 의미
9. 면접 + 자기 점검

1️⃣ 멀티태스킹의 정의와 메커니즘

1.1 멀티태스킹의 정의

멀티태스킹 (Multitasking):

  1개의 CPU 코어가 여러 작업 (태스크) 을
  매우 빠르게 번갈아 가며 실행하는 기법.

기반:
  - 소프트웨어 (OS 의 시분할 스케줄링)
  - 1개 코어로도 가능

효과:
  - 사람 눈에는 동시 실행처럼
  - 실제론 한 순간 1가지

1.2 메커니즘

멀티태스킹의 동작:

시간 →
  코어:  [작업A][작업B][작업C][작업A][작업B]...
         ↑      ↑      ↑
         10ms   10ms   10ms (예시)

  - 작업 A 를 10ms 실행
  - 작업 B 로 전환 (컨텍스트 스위칭)
  - 작업 B 를 10ms 실행
  - 작업 C 로 전환
  - ...

  매우 빠른 전환 (밀리초 단위)
  → 사람은 "동시 실행" 으로 인지

1.3 OS 의 역할

OS (운영체제) 가 멀티태스킹 관리:

1. 스케줄러 (Scheduler)
   - 어떤 작업을 얼마나 실행할지 결정

2. 컨텍스트 스위칭 (Context Switching)
   - 작업 A → B 전환 시 상태 저장/복원

3. 우선순위 (Priority)
   - 중요한 작업 먼저

4. 시간 할당량 (Time Quantum/Slice)
   - 각 작업에 주는 CPU 시간

1.4 역사적 배경

멀티태스킹의 역사:

초기 컴퓨터 (배치 처리):
  - 한 번에 1프로그램만
  - 끝나야 다음

멀티프로그래밍:
  - 여러 프로그램 메모리에
  - I/O 대기 시 다른 프로그램

시분할 시스템 (Time Sharing):
  - 여러 사용자가 동시 사용처럼
  - 1960년대 (CTSS, Multics)

현대:
  - 모든 OS 가 멀티태스킹
  - Windows, Linux, macOS

1.5 단일 코어의 멀티태스킹

단일 코어 시스템:

  CPU 코어 1개

  실행 중:
    한 순간에 1개 스레드/프로세스만
  
  멀티태스킹:
    빠르게 전환하여 여러 작업 진행
    → 동시 실행처럼 보임

  하지만:
    진짜 동시는 아님 (동시성, 병렬성 X)

1.6 ILIC 의 맥락

// ILIC 서버에서의 멀티태스킹
// 여러 요청을 1개 코어가 번갈아 처리 (단일 코어 가정)

@RestController
public class ShipmentController {
    
    @GetMapping("/api/shipments/{id}")
    public Shipment get(@PathVariable Long id) {
        // 요청 1 처리 중...
        // 잠시 후 요청 2 로 전환 (멀티태스킹)
        // 다시 요청 1 로...
        return service.findById(id);
    }
}

// 현대 멀티코어 서버:
// 여러 코어가 각자 요청 처리 (멀티프로세싱)
// + 각 코어가 여러 요청 번갈아 (멀티태스킹)

1.7 자기 점검 답변

멀티태스킹의 정의와 메커니즘은?

:
1. 정의:

  • 1개 코어가 여러 작업 번갈아
  • 소프트웨어 기법 (시분할)
  1. 메커니즘:

    • 빠른 전환 (밀리초)
    • 컨텍스트 스위칭
    • 사람 눈에는 동시 실행
  2. OS 역할:

    • 스케줄러
    • 컨텍스트 스위칭
    • 시간 할당량
  3. 본질:

    • 한 순간 1가지 (단일 코어)
    • 동시성 (Concurrency)

2️⃣ 멀티프로세싱의 정의와 메커니즘

2.1 멀티프로세싱의 정의

멀티프로세싱 (Multiprocessing):

  여러 개의 CPU 코어가
  실제로 동시에 작업을 실행하는 방식.

기반:
  - 하드웨어 (다중 코어/CPU)
  - 물리적 동시 실행

효과:
  - 진짜 동시 실행
  - 병렬성 (Parallelism)

2.2 메커니즘

멀티프로세싱의 동작:

시간 →
  코어1:  [작업A 계속 실행 ─────────]
  코어2:  [작업B 계속 실행 ─────────]
  코어3:  [작업C 계속 실행 ─────────]
  코어4:  [작업D 계속 실행 ─────────]

  - 4개 코어가 각자 다른 작업
  - 진짜 동시에
  - 4배 처리 (이론상)

2.3 멀티코어 CPU

현대 CPU 의 구조:

  CPU 패키지
    ├── 코어 1 (자체 ALU, 레지스터, L1/L2 캐시)
    ├── 코어 2
    ├── 코어 3
    ├── 코어 4
    └── 공유 L3 캐시

  각 코어가 독립적으로 실행
  → 진짜 병렬 처리

  예시:
    - Intel Core i7: 8코어
    - Apple M3: 8코어
    - 서버 CPU: 수십 코어

2.4 하이퍼스레딩 (Hyper-Threading)

하이퍼스레딩 (SMT):

  1개 물리 코어가 2개 논리 코어처럼.

원리:
  - 코어의 유휴 자원 활용
  - 1코어 = 2 스레드 동시 (논리적)

예시:
  - 4코어 + 하이퍼스레딩 = 8 논리 코어
  - Runtime.availableProcessors() = 8

주의:
  - 논리 코어 ≠ 물리 코어 성능
  - 2배 성능 X (보통 1.2~1.3배)

2.5 코어 수 확인

// 자바에서 코어 수 확인
int cores = Runtime.getRuntime().availableProcessors();
System.out.println("Available processors: " + cores);
// 물리 코어 또는 논리 코어 (하이퍼스레딩 포함)

// 활용 — 스레드 풀 크기 결정
int poolSize = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(poolSize);

// CPU 바운드 작업: 코어 수
// I/O 바운드 작업: 코어 수보다 많이

2.6 ILIC 의 맥락

// ILIC 의 병렬 처리
public class ShipmentBatchProcessor {
    
    private final int cores = Runtime.getRuntime().availableProcessors();
    
    // 멀티프로세싱 활용 — 병렬 Stream
    public void processBatch(List<Shipment> shipments) {
        shipments.parallelStream()   // 여러 코어 활용
            .forEach(this::processExpensive);
        // 4코어면 약 4배 빠름 (CPU 바운드 작업)
    }
    
    // 명시적 스레드 풀
    public void processWithPool(List<Shipment> shipments) {
        ExecutorService executor = Executors.newFixedThreadPool(cores);
        try {
            for (Shipment s : shipments) {
                executor.submit(() -> processExpensive(s));
            }
        } finally {
            executor.shutdown();
        }
    }
    
    private void processExpensive(Shipment s) {
        // CPU 무거운 작업
    }
}

2.7 자기 점검 답변

멀티프로세싱의 정의와 메커니즘은?

:
1. 정의:

  • 여러 코어가 실제 동시 실행
  • 하드웨어 기반
  1. 메커니즘:

    • 각 코어가 독립 작업
    • 진짜 동시
    • 병렬성
  2. 멀티코어:

    • 현대 CPU
    • 각 코어 독립 실행
  3. 하이퍼스레딩:

    • 1코어 → 2 논리 코어
    • 유휴 자원 활용
  4. 확인:

    • Runtime.availableProcessors()

3️⃣ 시분할 (Time Sharing) 의 원리

3.1 시분할의 정의

시분할 (Time Sharing):

  CPU 시간을 매우 작은 단위 (타임 슬라이스) 로 쪼개
  여러 작업에 빠르게 번갈아 할당하는 기법.

목적:
  - 멀티태스킹의 핵심
  - 여러 작업 "동시 진행" 환상

3.2 타임 슬라이스 (Time Slice)

타임 슬라이스 (Time Quantum):

  각 작업에 할당되는 CPU 시간.

크기:
  - 보통 수~수십 밀리초
  - Linux: 동적 (1ms ~ 수백 ms)

너무 크면:
  - 응답성 ↓ (다른 작업 오래 대기)

너무 작으면:
  - 컨텍스트 스위칭 오버헤드 ↑

3.3 시각화

시분할의 동작 (타임 슬라이스 10ms 예시):

시간 (ms):  0    10   20   30   40   50
작업:       [A ][B ][C ][A ][B ][C ]
            └─┬┘
              10ms 마다 전환

사람의 인지:
  - 100ms (1초의 1/10) 안에
  - A, B, C 가 여러 번 실행
  - "동시에 진행되는 것처럼"

실제:
  - 한 순간엔 1가지만
  - 빠른 전환의 착시

3.4 선점형 vs 비선점형

선점형 (Preemptive):
  - OS 가 강제로 작업 전환
  - 타임 슬라이스 소진 시
  - 현대 OS 표준
  - 한 작업이 독점 불가

비선점형 (Non-preemptive / Cooperative):
  - 작업이 자발적으로 양보
  - 옛 시스템 (Windows 3.x 등)
  - 한 작업이 양보 안 하면 멈춤
  - 위험

자바의 yield():
  - 비선점형 힌트 (자발적 양보)
  - 보장 X (OS 가 결정)

3.5 시분할의 환상

"동시 실행" 의 환상:

비유 — 영화 필름:
  - 영화는 정지 이미지 (프레임) 의 연속
  - 초당 24~60 프레임
  - 사람 눈은 연속 동작으로 인지

시분할:
  - 작업들의 빠른 전환
  - 사람 눈은 동시 실행으로 인지

핵심:
  - 실제론 순차 (1가지씩)
  - 빠르니 동시처럼

3.6 ILIC 의 맥락

// 단일 코어 서버에서 여러 요청 처리 (시분할)
// 톰캣의 스레드 풀

// server.tomcat.threads.max=200 (기본)
// 200개 스레드가 시분할로 요청 처리

@Service
public class ShipmentService {
    
    public Shipment process(Long id) {
        // 이 메서드 실행 중
        // 타임 슬라이스 소진 시 다른 요청으로 전환
        // (멀티태스킹)
        
        Shipment s = repository.findById(id);
        // DB 조회 (I/O 대기) 중 다른 요청 처리
        // → CPU 효율 ↑
        
        return s;
    }
}

3.7 자기 점검 답변

시분할의 원리는?

:
1. 정의:

  • CPU 시간을 잘게 쪼개
  • 여러 작업에 번갈아
  1. 타임 슬라이스:

    • 각 작업의 CPU 시간
    • 수~수십 ms
  2. 선점형:

    • OS 강제 전환
    • 현대 표준
  3. 환상:

    • 빠른 전환 = 동시 실행처럼
    • 영화 필름 비유
  4. 본질:

    • 실제론 순차
    • 동시성 (Concurrency)

4️⃣ 스케줄링의 역할

4.1 스케줄링의 정의

스케줄링 (Scheduling):

  어떤 작업을 언제, 얼마나 실행할지
  OS 의 스케줄러가 결정하는 것.

목적:
  - CPU 효율 최대화
  - 공정성
  - 응답성

4.2 스케줄러의 결정

스케줄러가 결정하는 것:

1. 다음에 실행할 작업
   - 우선순위
   - 대기 시간
   - 공정성

2. 실행 시간 (타임 슬라이스)
   - 작업 특성
   - 우선순위

3. 전환 시점
   - 타임 슬라이스 소진
   - I/O 대기
   - 우선순위 높은 작업 도착

4.3 스케줄링 알고리즘

주요 스케줄링 알고리즘:

1. FCFS (First Come First Served)
   - 먼저 온 순서
   - 단순, 비효율

2. Round Robin
   - 순환 할당
   - 공정

3. Priority Scheduling
   - 우선순위 기반
   - 기아 (starvation) 가능

4. CFS (Completely Fair Scheduler)
   - Linux 의 기본
   - 공정성 중심

5. MLFQ (Multi-Level Feedback Queue)
   - 여러 우선순위 큐
   - 동적 조정

4.4 자바와 스케줄링

// 자바 스레드 우선순위
Thread t = new Thread(task);
t.setPriority(Thread.MAX_PRIORITY);   // 10
t.setPriority(Thread.NORM_PRIORITY);  // 5 (기본)
t.setPriority(Thread.MIN_PRIORITY);   // 1

// 주의:
// - 우선순위는 힌트일 뿐
// - OS 스케줄러가 최종 결정
// - 플랫폼마다 동작 다름
// - 의존하면 안 됨

// yield - 양보 힌트
Thread.yield();
// "다른 스레드에 기회를" 힌트
// 보장 X

4.5 스케줄링과 컨텍스트 스위칭

스케줄링 결정 → 컨텍스트 스위칭

1. 스케줄러: "작업 A 그만, 작업 B 실행"
2. 컨텍스트 스위칭:
   - A 의 상태 저장 (레지스터, PC 등)
   - B 의 상태 복원
3. 작업 B 실행

비용:
  - 컨텍스트 스위칭은 오버헤드
  - 잦으면 성능 ↓ (다음 Unit 1.4)

4.6 ILIC 의 맥락

// 스레드 우선순위 — 신중히 사용
public class ShipmentScheduler {
    
    public void startWorkers() {
        // 중요 작업 (높은 우선순위 — 힌트)
        Thread critical = new Thread(this::processCritical);
        critical.setPriority(Thread.MAX_PRIORITY);
        critical.start();
        
        // 일반 작업
        Thread normal = new Thread(this::processNormal);
        normal.setPriority(Thread.NORM_PRIORITY);
        normal.start();
        
        // 백그라운드 (낮은 우선순위)
        Thread background = new Thread(this::cleanup);
        background.setPriority(Thread.MIN_PRIORITY);
        background.setDaemon(true);
        background.start();
    }
    
    // 실무: 우선순위보다 Executor + 적절한 풀 구성 권장
}

4.7 자기 점검 답변

스케줄링의 역할은?

:
1. 정의:

  • 어떤 작업을 언제/얼마나
  • OS 스케줄러 결정
  1. 결정 사항:

    • 다음 작업
    • 실행 시간
    • 전환 시점
  2. 알고리즘:

    • FCFS, Round Robin
    • Priority, CFS, MLFQ
  3. 자바:

    • 우선순위는 힌트
    • OS 가 최종 결정
    • 의존 X

5️⃣ 동시성 vs 병렬성

5.1 정의

동시성 (Concurrency):
  - 여러 작업을 번갈아 처리
  - 논리적 동시 (실제론 순차)
  - 단일 코어로도 가능
  - 멀티태스킹

병렬성 (Parallelism):
  - 여러 작업을 실제 동시 처리
  - 물리적 동시
  - 멀티 코어 필요
  - 멀티프로세싱

5.2 Rob Pike 의 명언

Rob Pike (Go 언어 창시자):

  "Concurrency is about dealing with lots of things at once.
   Parallelism is about doing lots of things at once."

번역:
  - 동시성: 여러 일을 다루는 것 (구조)
  - 병렬성: 여러 일을 동시에 하는 것 (실행)

핵심:
  - 동시성: 설계/구조 개념
  - 병렬성: 실행 방식

5.3 시각화

동시성 (단일 코어):
  코어:  [A][B][A][B][A][B]
         번갈아 처리
         논리적 동시

병렬성 (멀티 코어):
  코어1: [A ────────────]
  코어2: [B ────────────]
         실제 동시
         물리적 동시

동시성 + 병렬성 (멀티 코어 + 멀티태스킹):
  코어1: [A][C][A][C]
  코어2: [B][D][B][D]
         각 코어가 번갈아
         + 코어 간 병렬

5.4 비유 — 카페

동시성 (바리스타 1명):
  주문 A 받고 → 주문 B 받고 → A 커피 만들고 → B 커피
  - 1명이 여러 주문 다룸 (구조)
  - 번갈아

병렬성 (바리스타 2명):
  바리스타 1: 주문 A
  바리스타 2: 주문 B
  - 실제 동시

핵심:
  - 동시성: 여러 주문을 어떻게 다룰지 (구조)
  - 병렬성: 실제 몇 명이 동시에 (실행)

5.5 자바에서

// 동시성 — 여러 작업 구조화
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 100; i++) {
    final int id = i;
    executor.submit(() -> process(id));
}
// 100개 작업을 4 스레드로 처리 (동시성)
// 4코어면 병렬성도 (4개 동시 실행)

// 병렬성 — 명시적 병렬
List<Integer> result = numbers.parallelStream()
    .map(this::heavyComputation)
    .toList();
// 멀티 코어 활용 (병렬성)

5.6 언제 어느 것이 유효한가

동시성이 유효:
  - I/O 바운드 작업
  - 네트워크, DB, 파일
  - 대기 시간 동안 다른 작업
  - 단일 코어로도 효율 ↑

병렬성이 유효:
  - CPU 바운드 작업
  - 계산 집약적
  - 멀티 코어 필수
  - 작업 분할 가능

예:
  - 웹 서버 (많은 요청): 동시성
  - 이미지 처리 (계산): 병렬성
  - 대용량 데이터 분석: 병렬성

5.7 ILIC 의 맥락

public class ShipmentConcurrencyParallelism {
    
    // 동시성 — I/O 바운드 (DB 조회 등)
    public List<Shipment> fetchMany(List<Long> ids) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        // 10 스레드 (코어보다 많이 — I/O 대기 동안 효율)
        try {
            List<Future<Shipment>> futures = ids.stream()
                .map(id -> executor.submit(() -> repository.findById(id)))
                .toList();
            
            return futures.stream()
                .map(this::getResult)
                .toList();
        } finally {
            executor.shutdown();
        }
    }
    
    // 병렬성 — CPU 바운드 (계산)
    public BigDecimal calculateTotalFreight(List<Shipment> shipments) {
        return shipments.parallelStream()   // 멀티 코어
            .map(this::expensiveFreightCalculation)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    private Shipment getResult(Future<Shipment> f) {
        try { return f.get(); }
        catch (Exception e) { throw new RuntimeException(e); }
    }
    
    private BigDecimal expensiveFreightCalculation(Shipment s) {
        // CPU 무거운 계산
        return s.getWeight().multiply(BigDecimal.valueOf(1000));
    }
}

5.8 자기 점검 답변

동시성 vs 병렬성의 차이는?

:
1. 동시성:

  • 여러 작업 번갈아 (논리)
  • 단일 코어 가능
  • 구조 개념
  1. 병렬성:

    • 실제 동시 (물리)
    • 멀티 코어 필요
    • 실행 방식
  2. Rob Pike:

    • 동시성: dealing with (구조)
    • 병렬성: doing (실행)
  3. 유효한 경우:

    • 동시성: I/O 바운드
    • 병렬성: CPU 바운드

6️⃣ 단일 코어에서의 "동시 실행"

6.1 핵심 질문

질문:
  코어가 1개인데 어떻게 동시 실행처럼 보이나?

답:
  멀티태스킹 (시분할).
  빠르게 번갈아 → 동시 실행 환상.

6.2 메커니즘 재정리

단일 코어의 멀티태스킹:

1. 작업 A 실행 (10ms)
2. 컨텍스트 스위칭
3. 작업 B 실행 (10ms)
4. 컨텍스트 스위칭
5. 작업 A 실행 (10ms)
...

사람 인지:
  - 1초에 A, B 가 각 50번 실행
  - "동시 진행" 으로 느낌

실제:
  - 한 순간 1가지
  - 빠른 전환

6.3 I/O 대기의 활용

멀티태스킹이 진짜 유용한 순간 — I/O 대기:

작업 A 가 DB 조회 (I/O):
  - DB 응답 대기 (수십 ms)
  - 이 동안 CPU 는 놀고 있음

멀티태스킹:
  - A 가 대기하는 동안
  - 작업 B 실행
  - CPU 효율 ↑

핵심:
  - I/O 대기 = CPU 유휴
  - 그 시간에 다른 작업
  - 단일 코어도 효율 ↑

6.4 시각화 — I/O 대기 활용

멀티태스킹 없이 (순차):
  코어:  [A 계산][A DB 대기...][A 계산][B 계산]...
                  ↑ CPU 놀고 있음 (낭비)

멀티태스킹 (효율):
  코어:  [A 계산][B 계산][A DB 완료 후][B DB 대기 중 C]...
                ↑ A 가 DB 대기하는 동안 B 실행
                
  → CPU 쉬지 않음
  → 처리량 ↑

6.5 단일 코어 vs 멀티 코어

단일 코어 + 멀티태스킹:
  - 동시성 O
  - 병렬성 X
  - I/O 바운드에 효과적
  - CPU 바운드는 한계

멀티 코어 + 멀티프로세싱:
  - 동시성 O
  - 병렬성 O
  - CPU 바운드도 효과적
  - 진짜 동시 실행

6.6 웹 서버의 예

웹 서버 (단일 코어 가정):

요청 100개 동시 도착:
  - 각 요청 = DB 조회 (I/O 대기 많음)
  - 멀티태스킹으로 처리
  - A 가 DB 대기 → B 처리 → C 처리...
  - CPU 효율 ↑
  - 100개를 1코어로도 처리 가능

핵심:
  - 웹 요청은 대부분 I/O 바운드
  - 단일 코어 + 멀티태스킹으로 충분히 많은 요청
  - 멀티 코어면 더 좋음

6.7 ILIC 의 맥락

// 단일 코어에서도 효율적인 비동기 I/O
public class ShipmentAsyncService {
    
    // I/O 바운드 — 멀티태스킹 효과
    public CompletableFuture<Shipment> fetchAsync(Long id) {
        return CompletableFuture.supplyAsync(() -> {
            // DB 조회 (I/O 대기)
            // 이 동안 다른 작업 처리 (멀티태스킹)
            return repository.findById(id);
        });
    }
    
    // 여러 요청 동시 (단일 코어도 효율)
    public CompletableFuture<List<Shipment>> fetchMany(List<Long> ids) {
        List<CompletableFuture<Shipment>> futures = ids.stream()
            .map(this::fetchAsync)
            .toList();
        
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream()
                .map(CompletableFuture::join)
                .toList());
    }
}

6.8 자기 점검 답변

단일 코어에서 동시 실행처럼 보이는 이유는?

:
1. 멀티태스킹:

  • 빠른 전환 (시분할)
  • 동시 실행 환상
  1. I/O 대기 활용:

    • 작업 A 가 I/O 대기
    • 그 동안 작업 B
    • CPU 효율 ↑
  2. 단일 코어 효과:

    • 동시성 O, 병렬성 X
    • I/O 바운드에 효과적
    • 웹 서버 (많은 요청)
  3. 본질:

    • 한 순간 1가지
    • 빠른 전환

7️⃣ 멀티태스킹 + 멀티프로세싱 함께

7.1 함께 사용 가능

질문:
  멀티태스킹과 멀티프로세싱을 함께 쓸 수 있나?

답:
  예! 현대 시스템의 표준.
  
  멀티 코어 (멀티프로세싱)
    + 각 코어가 여러 작업 번갈아 (멀티태스킹)

7.2 현대 시스템의 모습

현대 시스템 (4코어 + 멀티태스킹):

  코어1:  [A][E][A][E]...   ← 멀티태스킹
  코어2:  [B][F][B][F]...   ← 멀티태스킹
  코어3:  [C][G][C][G]...   ← 멀티태스킹
  코어4:  [D][H][D][H]...   ← 멀티태스킹
          ↑
          코어 간 병렬 (멀티프로세싱)

8개 작업 (A~H):
  - 4코어로 병렬 (멀티프로세싱)
  - 각 코어가 2작업 번갈아 (멀티태스킹)

7.3 OS 의 스케줄링

OS 의 작업 분배:

1. 작업을 코어에 할당
   - 로드 밸런싱
   - 코어 친화도 (affinity)

2. 각 코어에서 멀티태스킹
   - 시분할
   - 컨텍스트 스위칭

3. 작업 이동
   - 한 코어가 바쁘면 다른 코어로
   - work stealing 유사

7.4 자바 스레드와 OS 스레드

자바 스레드 ↔ OS 스레드:

전통적 (1:1 모델):
  - 자바 스레드 1개 = OS 스레드 1개
  - OS 가 스케줄링
  - 멀티 코어 활용

Virtual Thread (Java 21+):
  - 다수 가상 스레드 = 소수 OS 스레드
  - M:N 모델
  - I/O 바운드에 효율
  - 멀티태스킹의 자바 레벨 구현

7.5 코어 수와 스레드 수

이상적 스레드 수:

CPU 바운드 작업:
  - 스레드 수 = 코어 수
  - 더 많으면 컨텍스트 스위칭 낭비

I/O 바운드 작업:
  - 스레드 수 > 코어 수
  - I/O 대기 동안 다른 스레드
  - 공식: 코어 수 × (1 + 대기시간/처리시간)

예:
  4코어, I/O 대기 90%:
  4 × (1 + 9) = 40 스레드 정도

7.6 ILIC 의 맥락

public class ShipmentThreadConfig {
    
    private final int cores = Runtime.getRuntime().availableProcessors();
    
    // CPU 바운드 풀 (코어 수)
    public ExecutorService cpuBoundPool() {
        return Executors.newFixedThreadPool(cores);
        // 계산 작업 (운임 계산 등)
    }
    
    // I/O 바운드 풀 (코어 수보다 많이)
    public ExecutorService ioBoundPool() {
        return Executors.newFixedThreadPool(cores * 4);
        // DB 조회, API 호출 등
    }
    
    // Virtual Thread (Java 21+) — I/O 바운드 최적
    public ExecutorService virtualThreadPool() {
        return Executors.newVirtualThreadPerTaskExecutor();
        // 수만 개 동시 I/O 작업 효율
    }
}

7.7 자기 점검 답변

멀티태스킹과 멀티프로세싱을 함께?

:
1. 함께 가능:

  • 현대 표준
  • 멀티 코어 + 멀티태스킹
  1. 현대 시스템:

    • 여러 코어 (병렬)
    • 각 코어가 번갈아 (멀티태스킹)
  2. OS 역할:

    • 코어 할당
    • 각 코어 시분할
    • 로드 밸런싱
  3. 스레드 수:

    • CPU 바운드: 코어 수
    • I/O 바운드: 더 많이

8️⃣ 실무에서의 의미

8.1 스레드 풀 크기 결정

// 작업 유형에 따른 풀 크기

// CPU 바운드 — 코어 수
int cpuPoolSize = Runtime.getRuntime().availableProcessors();

// I/O 바운드 — 더 많이
int ioPoolSize = Runtime.getRuntime().availableProcessors() * 4;

// 혼합 — 측정 후 조정
// Little's Law: 스레드 수 = 처리율 × 응답시간

8.2 과도한 스레드의 위험

스레드를 무한정 늘리면?

1. 메모리 부족
   - 스레드 1개 ≈ 1MB 스택
   - 1만 스레드 = 10GB

2. 컨텍스트 스위칭 폭증
   - 전환 비용 ↑
   - 실제 작업 시간 ↓

3. 성능 저하
   - 코어 수보다 많으면
   - 오히려 느려짐

결론:
  - 적절한 풀 크기
  - 무한 생성 X (Executor)

8.3 동시성 vs 병렬성 선택

// I/O 바운드 — 동시성 (많은 스레드)
public List<Response> callManyApis(List<Request> requests) {
    ExecutorService executor = Executors.newFixedThreadPool(50);
    // I/O 대기 많으니 스레드 많이
    // ...
}

// CPU 바운드 — 병렬성 (코어 수)
public List<Result> heavyCompute(List<Data> data) {
    return data.parallelStream()   // ForkJoinPool (코어 수)
        .map(this::compute)
        .toList();
}

8.4 측정의 중요성

"추측하지 말고 측정하라":

1. 프로파일링
   - VisualVM, JProfiler
   - 스레드 상태 확인

2. 벤치마크
   - JMH
   - 실제 성능 측정

3. 모니터링
   - 운영 환경
   - CPU, 메모리, 스레드 수

핵심:
  - 동시성/병렬성 효과는 측정으로
  - 무조건 병렬 X (오버헤드)

8.5 ILIC 의 실무 패턴

@Configuration
public class ConcurrencyConfig {
    
    @Bean
    public Executor cpuBoundExecutor() {
        int cores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cores, cores,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
    
    @Bean
    public Executor ioBoundExecutor() {
        int cores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cores * 2, cores * 8,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
}

@Service
public class ShipmentProcessingService {
    
    private final Executor cpuExecutor;
    private final Executor ioExecutor;
    
    // CPU 바운드 — 운임 계산
    @Async("cpuBoundExecutor")
    public CompletableFuture<BigDecimal> calculateFreight(Shipment s) {
        return CompletableFuture.completedFuture(expensiveCalc(s));
    }
    
    // I/O 바운드 — 외부 API
    @Async("ioBoundExecutor")
    public CompletableFuture<TrackingInfo> fetchTracking(String blNo) {
        return CompletableFuture.completedFuture(externalApi.track(blNo));
    }
}

8.6 자기 점검 답변

실무에서의 의미는?

:
1. 스레드 풀 크기:

  • CPU 바운드: 코어 수
  • I/O 바운드: 더 많이
  1. 과도한 스레드 위험:

    • 메모리 부족
    • 컨텍스트 스위칭 폭증
    • 성능 저하
  2. 선택:

    • I/O: 동시성 (많은 스레드)
    • CPU: 병렬성 (코어 수)
  3. 측정:

    • 추측하지 말고 측정
    • 프로파일링, 벤치마크

9️⃣ 면접 + 자기 점검

9.1 면접 단골 질문 매핑

Q핵심 답변
멀티태스킹?1코어가 여러 작업 번갈아 (시분할)
멀티프로세싱?여러 코어가 실제 동시
시분할?CPU 시간 쪼개 번갈아 할당
동시성 vs 병렬성?번갈아 (논리) vs 동시 (물리)
단일 코어 동시 실행?멀티태스킹 (환상)
함께 사용?가능 (현대 표준)
컨텍스트 스위칭?작업 전환 비용
스레드 수 결정?CPU 코어 수 vs I/O 더 많이
availableProcessors?코어 수 (논리 포함)
우선순위?힌트, OS 가 결정

9.2 자기 점검 체크리스트

멀티태스킹

  • 정의 (1코어 번갈아)
  • 시분할 메커니즘
  • 소프트웨어 기반

멀티프로세싱

  • 정의 (여러 코어 동시)
  • 하드웨어 기반
  • 하이퍼스레딩

시분할

  • 타임 슬라이스
  • 선점형 vs 비선점형
  • 동시 실행 환상

동시성 vs 병렬성

  • 논리 vs 물리
  • Rob Pike 명언
  • I/O vs CPU 바운드

실무

  • 스레드 풀 크기
  • 과도한 스레드 위험
  • 측정

9.3 추가 심화 질문

Q1: 컨텍스트 스위칭의 정확한 비용?

답:

  • 직접 비용: 레지스터 저장/복원 (수 μs)
  • 간접 비용: 캐시 미스, TLB 플러시
  • 빈번하면 누적 큼
  • 다음 Unit 1.4 에서 정밀

Q2: GIL (Global Interpreter Lock) 과 자바?

답:

  • Python 의 GIL: 한 번에 1스레드만 (병렬 X)
  • 자바: GIL 없음
  • 자바는 진짜 멀티스레드 병렬
  • CPU 바운드도 멀티 코어 활용

Q3: Amdahl's Law?

답:

  • 병렬화 가능 부분의 비율로 최대 속도 향상 한계
  • speedup = 1 / ((1-P) + P/N)
  • P: 병렬 가능 비율, N: 코어 수
  • 순차 부분이 병목
  • 코어 무한정 늘려도 한계

Q4: I/O 바운드 vs CPU 바운드 판단?

답:

  • CPU 바운드: 계산 위주 (이미지 처리, 암호화)
  • I/O 바운드: 대기 위주 (DB, 네트워크, 파일)
  • 프로파일링으로 확인
  • CPU 사용률 보면 판단 가능

Q5: Virtual Thread (Java 21+) 의 의미?

답:

  • 경량 스레드 (가상)
  • 다수 가상 스레드 = 소수 OS 스레드
  • I/O 바운드에 효율
  • 자바 레벨 멀티태스킹
  • 수만 개 동시 I/O 작업 가능

🎯 핵심 요약 — 3줄 정리

1. 멀티태스킹 vs 멀티프로세싱

  • 멀티태스킹: 1코어 번갈아 (시분할, 소프트웨어)
  • 멀티프로세싱: 여러 코어 동시 (하드웨어)

2. 동시성 vs 병렬성

  • 동시성: 번갈아 (논리), 단일 코어 가능
  • 병렬성: 동시 (물리), 멀티 코어 필요

3. 실무

  • 함께 사용 (현대 표준)
  • CPU 바운드: 코어 수, I/O 바운드: 더 많이
  • 측정 우선

📚 다음으로...

Unit 1.2 — 프로세스와 스레드

이번 Unit에서 멀티태스킹/멀티프로세싱을 봤다면, 다음은 프로세스와 스레드의 정의와 메모리 구조.

  • 프로세스의 정의
  • 스레드의 정의
  • 메모리 구성 (공유 vs 개별)
  • 클래스/인스턴스 비유

Phase 1 진행 상황

🚀 Phase 1 — 동시성의 기초
  ✅ Unit 1.1 멀티태스킹 vs 멀티프로세싱 ← 여기
  ⏭ Unit 1.2 프로세스와 스레드
  ⏭ Unit 1.3 변수 종류와 메모리 위치
  ⏭ Unit 1.4 스케줄링 큐와 컨텍스트 스위칭

4주차 누적 진행

🚀 Phase 1 — 동시성의 기초 (1/4 진행)

총: 1/35 Unit

🚀 4주차 시작 — 동시성의 세계 진입

profile
Software Developer

0개의 댓글