본문 바로가기
역량 UP!/Architecture

동시성(Concurrency) 모델&스케줄링 모델(feat. ForkJoinPool)

by 태하팍 2025. 11. 4.
반응형

2025.08.16 - [역량 UP!/Architecture] - C10K 문제(C10k Problem)란 무엇인가?

Concurrency Models 5가지

동시성 모델 설명 대표 기술
Thread-based 여러 스레드가 공유 메모리 사용 Java Threads, ThreadPool, Spring MVC
Event-driven (이벤트 루프 기반) 1개 또는 소수 스레드가 이벤트를 감지하고 처리 Node.js, Redis, Netty, Nginx
Message-passing / CSP 공유 메모리 X, 채널로만 통신 Go Goroutine + channel
Actor Model 모든 게 Actor, 메시지로만 통신 Akka, Erlang
Reactive Streams Non-blocking + Backpressure WebFlux, Project Reactor

위 동시성모델에서 나올수 있는게  Application 스케줄링 모델(작업 실행 전략)이 있습니다.(=OS 스케줄링 아님 주의!)

Application Scheduling Models 3가지

스케줄링 설명 예시
Event Loop 1개의 쓰레드가 이벤트를 감시 → 하나씩 처리 Redis, Node.js, Netty
Work Sharing (공유 큐 방식) 작업을 중앙 큐에 넣고 여러 쓰레드가 꺼내서 처리 ThreadPoolExecutor, Spring @Async
Work Stealing (작업 훔치기) 쓰레드마다 큐를 가짐 → 일이 없으면 남의 큐에서 훔쳐옴 ForkJoinPool, Akka Dispatcher, parallelStream
  • Akka에서 Dispatcher가 ForkJoinPool + Scheduler 기능을 합니다.
    • Dispatcher : MailBox(Queue)에서 메시지를 꺼내고 Actor 실행을 스케줄링 합니다.

ForkJoinPool

Work Sharing 모델은 ThreadPool에서 Thread를 관리하며 작업을 처리하는 방식인데
작업 수가 많아질수록 모든 Thread가 중앙큐에 접근하려고 하면서 경쟁이 발생하고
한 쓰레드가 작업이 끝날때까지 대기하거나 Idle상태가 될수 있습니다.
즉, 큐가 병목지점이 되면서 지연(latency)이 발생 합니다.

이러한 문제를 해결하기 위해 Work Stealing 방식이 나오게 됩니다.
Java의 ForkJoinPool이 대표적인 구현체 입니다.

특징으로 각 쓰레드가 자기 큐를 자기고 있고 자기자신의 큐의 작업부터 처리 합니다.
만약 어떤 쓰레드가 할 일이 없어지면 그냥 대기타고 있는게 아니라 다른 쓰레드의 큐에서 남은 작업을 뒤에서 몰래 훔쳐와서 처리 합니다.

각각의 큐를 가지고 있기 때문에 lock이 걸리지 않고 쓰레드들이 놀지 않고 서로의 일을 서로 나눠가지고
작업을 쪼개서(fork) 병렬로 처리하고 끝나면 다시 모으는(join) 구조 덕분에 작은 작업에도 좋습니다.

그래서 Akka의 Dispatcher, Java Parallel Steam, CompletableFuture, Kotlin Coroutine의 내부 스케줄러들도
Work Stealing(ForkJoinPool)기반으로 동작 합니다.

ForkJoinPoll 구조도

  • Client에서 task를 submit하면 in-bound Queue에 들어가고
  • 해당 task를 Worker #2 쓰레드가 가져와서 fork를 합니다.
    • 이때 쪼개진 부모 task는 쓰레드의 호출 스택(Stack Frame)에 존재 합니다.
    • 자식1,2 task는 Worker #2 Deque에 push 됩니다.
  • Work #3 쓰레드의 Deque가 비어있다면 Worker #2의 자식task2를 하나 훔쳐(steal)옵니다.
  • 부모 task는 자식 task들을 기다렸다가 join 합니다. 

 

참고 : https://www.codelatte.io/courses/java_programming_basic/KUYNAB4TEI5KNSJV

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinTask.html

https://hg.openjdk.org/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/concurrent/ForkJoinTask.java

반응형