페이지 불러오는 중
스레드 풀을 쓰는 이유를 한 문장으로: 스레드를 매번 만들고 없애며 개수를 사람이 조정하는 대신, core / max / queue / keepAlive / 거절 정책을 한 번 계약으로 정해 두면 풀이 부하에 따라 스레드를 자동으로 늘리고 줄이기 때문.
이 노트는 플랫폼 스레드를 관리하는 풀(ThreadPoolExecutor) 을 다룹니다.
스레드 풀 = [재사용 스레드 집합] + [대기 작업 큐] + [스케일링·거절 정책]
└ 빌려 쓰고 반납 └ 처리 못한 작업 └ 언제 늘리고 줄이고 거절할지new Thread() 반복의 비용Xss 기본 512KB~1MB).OutOfMemoryError: unable to create new native thread 또는 시스템 전체 불안정.ThreadPoolExecutor의 생성자 파라미터가 곧 계약서입니다.
이 값들을 한 번 정하면, 이후 스레드 증감은 풀이 알아서 합니다.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
10, // corePoolSize
50, // maximumPoolSize
60L, TimeUnit.SECONDS, // keepAliveTime: 유휴 60초 후 초과분 반납
new ArrayBlockingQueue<>(1_000), // 유한 큐
namedFactory("worker-"),
new ThreadPoolExecutor.CallerRunsPolicy() // 포화 시 제출 스레드가 직접 실행
);corePoolSize → 새 코어 스레드 생성해 즉시 실행.maximumPoolSize → 임시 스레드 생성해 실행.maximumPoolSize까지 늘었던 초과 스레드가 keepAliveTime 동안 놀면 회수됩니다(코어까지 축소).allowCoreThreadTimeOut(true) 를 켜면 코어 스레드도 유휴 시 반납되어 0까지 축소 가능.Executors.newFixedThreadPool() = 무제한 LinkedBlockingQueue → 큐가 힙을 잠식하다 지연된 OOM.Executors.newCachedThreadPool() = SynchronousQueue + Integer.MAX_VALUE max → 스레드 폭증 위험.Executors.* 팩토리 메서드는 숨은 무제한(큐 또는 스레드)이 있으므로 운영 코드에서 지양.ThreadPoolExecutor를 직접 구성하거나, Spring의 ThreadPoolTaskExecutor에 값을 명시.getActiveCount(), getQueue().size(), getCompletedTaskCount(), 거절 카운트 → 대시보드/알람 연결.코어 수 × (1 + 대기시간/서비스시간).maximumPoolSize)보다 크면, 초과 워커는 커넥션 대기로 묶입니다. 두 상한을 함께 봅니다. → Java - 대용량 트래픽·비동기 병렬 환경의 메모리·리소스 한계 관리가상 스레드(Java 21, newVirtualThreadPerTaskExecutor()): 스레드가 매우 가벼워 "스레드 수 상한"이라는 관리 항목 자체가 사라집니다.
그러나 다운스트림 동시성 제한(Semaphore 등)은 여전히 필요 — 백만 개의 가상 스레드가 커넥션 풀에 몰리면 그대로 병목입니다. → Java Virtual Threads - Pinning 현상과 해결 방안
ForkJoinPool.commonPool(): parallelStream이 공유하므로, 여기서 블로킹 작업을 하면 전역 영향. CPU 바운드 계산에 한정.
Bulkhead: 기능별로 풀을 분리해, 한 기능의 폭주가 전체를 죽이지 않도록 격벽.
new LinkedBlockingQueue<>()), newFixedThreadPool/newCachedThreadPool 무비판 사용maximumPoolSize만 키우고 큐는 무제한 → max가 동작하지 않음Executor 생성(풀의 의미 상실) 후 shutdown() 누락ThreadLocal 사용 후 remove() 누락 → 누수
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.