자바 언어·플랫폼 용어 사전
동시성ExecutorService · 스레드 풀 · CompletableFuture

Executor

스레드를 직접 만들지 않고 작업을 제출하는 방식. 풀 크기와 거부 정책이 안정성을 좌우한다.

스레드를 직접 만드는 대신 작업(Runnable·Callable)을 제출하고 실행은 풀에 맡기는 구조.

왜 직접 만들지 않나

// ❌ 요청마다 스레드를 만든다
new Thread(() -> handle(req)).start();
  • 생성·소멸 비용이 매번 든다 (OS 스레드는 비싸다)
  • 개수에 상한이 없다 → 트래픽이 늘면 OOM: unable to create new native thread
  • 우선순위·이름·예외 처리를 매번 챙겨야 한다

풀을 직접 구성한다

var pool = new ThreadPoolExecutor(
    10,                                    // corePoolSize
    20,                                    // maximumPoolSize
    60L, TimeUnit.SECONDS,                 // 유휴 스레드 생존 시간
    new ArrayBlockingQueue<>(500),         // 유한 큐 ← 중요
    new ThreadPoolExecutor.CallerRunsPolicy());   // 거부 정책

작업이 들어오면

  • ① core 미만이면 새 스레드를 만든다
  • ② core 가 찼으면 큐에 넣는다
  • ③ 큐도 찼으면 max 까지 스레드를 늘린다
  • ④ 그것도 안 되면 거부 정책을 실행한다

②가 ③보다 먼저라는 게 직관과 다르다 — 큐가 무한이면 스레드는 core 이상 절대 안 늘어난다.

Executors 팩토리의 함정

Executors.newFixedThreadPool(10);   // 큐가 무한(LinkedBlockingQueue) → 쌓이다 OOM
Executors.newCachedThreadPool();    // 스레드 상한이 Integer.MAX_VALUE → 스레드 폭증

둘 다 상한이 없다. 그래서 실무에서는 ThreadPoolExecutor를 직접 구성하는 것이 권장된다.

거부 정책

  • AbortPolicy(기본) — RejectedExecutionException 을 던진다
  • CallerRunsPolicy — 제출한 스레드가 직접 실행한다 → 자연스러운 배압(backpressure)
  • DiscardPolicy — 조용히 버린다
  • DiscardOldestPolicy — 큐의 가장 오래된 것을 버리고 넣는다

CallerRunsPolicy 는 제출 속도를 늦춰 시스템이 스스로 브레이크를 잡게 한다 — 유실이 허용되지 않는 곳의 실용적 선택이다.

풀 크기

  • CPU 바운드 — 코어 수 + 1 근처
  • I/O 바운드 — 더 크게 — 대기 시간 비율만큼 늘린다
    • (다만 가상 스레드가 있으면 이 계산 자체가 필요 없어진다)

결과를 조합할 때

CompletableFuture<A> a = CompletableFuture.supplyAsync(this::loadA, pool);
CompletableFuture<B> b = CompletableFuture.supplyAsync(this::loadB, pool);
a.thenCombine(b, this::merge).join();      // 둘을 병렬로 부르고 합친다

supplyAsync에 풀을 안 넘기면 공용 ForkJoinPool을 쓴다 — 코어 수 - 1 크기이고 다른 병렬 스트림과 공유되므로, 블로킹 작업을 넣으면 전체가 막힌다.

반드시 종료한다

pool.shutdown();                                   // 새 작업은 거부, 진행 중인 건 끝낸다
if (!pool.awaitTermination(30, TimeUnit.SECONDS))
    pool.shutdownNow();                            // 강제 중단

면접 함정

  • "newFixedThreadPool이 안전하다" → 큐가 무한이라 메모리가 터진다.
  • "풀 크기를 크게 하면 빨라진다" → 컨텍스트 스위칭이 늘어 어느 지점부터 느려진다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 동시성