-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
📌 [아이템 80] 스레드보다는 실행자, 태스크, 스트림을 애용하라
✨ 핵심 내용
concurrent 패키지
이전의 코드들은 안전 실패나 응답 불가의 여지를 고려하면서 해야했음
하지만 java.util.concurrent 패키지의 등장으로 이런 코드들이 줄어들 수 있었다
- 이미 패지키지 내에 구현이 되어있기 때문
해당 패키지에는 실행자 프레임워크(Executor Framework)라고 하는 인터페이스 기반의 유연한 태스크 실행 기능이 담겨있다
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(runnable); // 실행자에 task 넘기기
exec.shutdown(); // 실행자 이쁘게 종료실행자 서비스 주요 기능
- 특정 태스크가 완료되기를 기다린다(코드 79-2에서 본 get 메서드)
- 태스크 모음 중 아무것 하나(invokeAny 메서드) 혹은 모든 태스크(invokeAll 메서드)가 완료되기를 기다린다
- 실행자 서비스가 종료하기를 기다린다(awaitTermination 메서드)
- 완료된 태스크들의 결과를 차례로 받는다(ExecutorCompletionService 이용)
- 태스크를 특정 시간에 혹은 주기적으로 실행하게 한다(ScheduledThread
PoolExecutor 이용)
큐를 둘 이상의 스레드가 처리하게 하고 싶다면?
→ 다른 정적 팩토리를 이용해 다른 종류의 실행자 서비스(스레드 풀)를 생성
ThreadPoolExecutor 클래스를 사용해 스레드 풀 동작의 모든 설정 가능
Thread Pool 결정 기준
| 항목 | Executors.newCachedThreadPool() |
Executors.newFixedThreadPool() 또는 ThreadPoolExecutor 직접 사용 |
|---|---|---|
| 설정의 복잡도 | 간단 (별도 설정 없음) | 중간 ~ 복잡 (스레드 수, 큐, 정책 등 직접 설정 가능) |
| 적합한 용도 | 작은 프로그램, 가벼운 서버 | 무거운 프로덕션 서버 |
| 태스크 처리 방식 | 큐에 쌓지 않고 즉시 실행, 가용 스레드 없으면 새로 생성 | 큐에 쌓고, 설정된 수만큼만 스레드 생성 |
| 스레드 수 제어 | 불가능 (필요할 때마다 계속 생성) | 가능 (최대 스레드 수 고정 또는 유동적 설정 가능) |
| 위험 요소 | 과도한 스레드 생성 → CPU 100% → 서버 과부하 | 설정에 따라 완전한 통제 가능, 안정적인 서버 운영 가능 |
| 권장 상황 | 실험적, 비중요 서비스, 낮은 요청량 | 고성능, 고트래픽의 프로덕션 환경 |
Runnable 과 Callable
Thread의 작업 단위를 나타내는 핵심 개념인 task의 두가지 종류
Callable은 Runnable과 같지만 값을 반환하고, 임의의 예외를 던질 수 있음
그리고 이 태스크를 수행하는 것이 실행자 서비스
fork-join task
- 큰 작업을 여러 작은 작업(Fork)으로 나눔
- 각각 병렬로 실행한 뒤 결과를 합침(Join)
- 남는 스레드는 다른 스레드의 작업을 대신 수행함 (Work Stealing)
장점
- CPU 코어를 최대한 활용 → 처리 속도 향상, 지연시간 감소
단점
- 직접 구현은 복잡 →
parallelStream()으로 간단히 활용 가능 - 단, 작게 나눌 수 있고 독립적인 작업에만 적합
💡 새롭게 알게 된 점
- 학습하면서 이전과 다르게 이해한 점이나 새롭게 알게 된 개념을 공유해주세요.
📚 정리
- 핵심 내용을 정리해주세요.
- 해당 개념을 실제 프로젝트에서 어떻게 활용할 수 있을지 아이디어를 공유해주시면 더 좋아요!
📢 댓글로 각자의 학습 내용을 공유해주세요!