Thread Pool (스레드 풀)
우리는 프로그램을 개발할 때 다양한 이유로 스레드를 생성한다. 비동기 처리의 일환일 수도 있고, 연산 처리의 효율을 높이기 위함일 수도 있다. 하지만 스레드를 계속해서 생성하고 회수하는 일은 시스템적으로 오버헤드가 상당히 큰 일이다.
스레드를 한 번 생성할 때마다 OS가 해당 스레드를 위한 메모리 영역(스택 등)을 확보해 주고, 스레드가 더 이상 필요 없을 땐 다시 이 메모리 영역을 회수하는 작업이 일어난다. 이 작업은 상당히 큰 비용이 발생하는 작업이기 때문에, 스레드를 계속 생성하고 회수하는 작업을 하게 되면 퍼포먼스에 영향을 끼칠 수밖에 없다.
위와 같은 문제를 해결하기 위해 등장한 것이 바로 Thread Pool이다. 스레드가 모여있는 풀장이라고 생각하면 되는데, 스레드가 필요할 때 생성하고 회수하는 것이 아니라, 미리 만들어진 풀장에 있는 스레드 중에서 적절한 스레드에게 작업을 할당하는 것이다.
위와 같은 방법으로 스레드를 생성하고 회수하는 작업을 줄여 오버헤드를 대폭 감소시킬 수 있다.
동작원리
새로운 작업이 들어올 때마다 'Task Queue'에 순서대로 삽입한다. 그리고 Thread Pool에 있는 적절한 스레드에 Task Queue에 있는 작업들을 하나씩 할당해 준다. 작업을 마친 스레드는 콜백 형태로 작업을 요청한 주체에게 결과를 반환한다.
(적절한 스레드라는 것은 현재 작업을 수행하고 있지 않고 쉬고 있는 스레드를 말하기도 한다.)
장단점
장점
퍼포먼스 저하 방지
스레드를 계속해서 생성하고 회수하는 작업을 수행하지 않기 때문에 해당 비용이 감소하여 퍼포먼스의 성능 저하를 방지할 수 있다.
다수의 요청을 효율적으로 처리
이곳저곳에서 요청이 들어올 수 있다. 여러 사용자들이 한 번에 요청을 보낼 때 Thread Pool을 사용한다면 빠르고 효율적으로 동시 작업을 처리할 수 있다.
단점
적절한 스레드 수의 딜레마
스레드를 미리 만들어 놓고 이를 활용하는 솔루션이다. 결국 스레드를 얼마나 만들어야 할지를 결정해야 하는데, 이 과정에서 너무 많은 스레드를 만들게 된다면 메모리만 차지하고 아무런 작업을 하지 않는 스레드가 발생할 가능성이 크다
하지만 스레드 개수가 충분치 못하게 구성되면 리소스 경합이 발생해 성능에 문제가 발생할 수도 있다.
종류
java.util.concurrent 패키지를 사용하여 Thread Pool을 생성할 수 있다.
- Executor 클래스
- ExecutorService 인터페이스
초기 스레드 수 : ExecutorService 객체가 생성될 때 기본적으로 생성되는 스레드의 수
코어 스레드 수 : 스레드가 증가한 후 사용되지 않은 스레드를 Thread Pool에서 제거할 때 최소한으로 유지해야 할 스레드의 수
최대 스레드 수 : Thread Pool에서 관리할 수 있는 최대 스레드의 수
Cached Thread Pool
ExecutorService executorService = Executors.newCachedThreadPool();
- 초기 스레드 수, 코어 스레드 수는 0개이고 최대 스레드 수는 integer 데이터 타입이 가질 수 있는 최댓값이다.(Integer.MAX_VALUE)
- 필요에 따라 스레드를 생성하며, 이전에 사용했던 스레드가 존재하다면 이를 재사용함
- 비동기 작업에 좋은 Thread Pool
- 스레드 개수보다 작업 개수가 많으면 새로운 스레드를 생성하여 작업을 처리한다.
- 만약 60초 동안 아무 일을 하지 않으면 스레드를 종료시키고 Thread Pool에서 제거한다.
Fixed Thread Pool
ExecutorService executorService = Executors.newFixedThreadPool(int nThreads);
- 초기 스레드 수는 0개, 코어 스레드 수와 최대 스레드 수는 매개변수 nThreads의 값으로 지정한다.
- 고정된 개수의 스레드를 생성하고, 모든 스레드가 작업 중이라면 Task Queue에 작업을 적재한다.
- 실패 시 새로운 스레드를 생성하여 대체한다.
- 아무 일을 하지 않아도 스레드를 제거하지 않는다.
Wokr Stealing Thread Pool
ExecutorService executorService = Executors.newWorkStealingPool(int parallelism);
- Java 8에서 새로 생긴 Thread Pool이다.
- 스레드를 동적으로 늘리고 줄일 수 있음
- 요청한 병렬 동작을 지원할 만큼 충분한 스레드 수를 유지하고, 스레드마다 독립적인 큐를 사용
- 작업 큐가 비어있는 스레드가 존재한다면, 다른 스레드의 작업 큐에서 작업을 Steal 해서 작업을 진행
- 위 특성 때문에, 작업의 순서를 보장하지 않음
참조
https://velog.io/@jsj3282/Threadpool#2-threadpool%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90
'멋진 개발자 > Java & Spring' 카테고리의 다른 글
개발자 성장 기록 45 - String, StringBuffer, StringBuilder (0) | 2024.04.09 |
---|---|
개발자 성장 기록 44 - Concurrent Collection (0) | 2024.04.08 |
개발자 성장 기록 42 - Java 직렬화 (Serialization) (0) | 2024.04.06 |
개발자 취준 기록 39 - Java 버전 별 차이 (0) | 2024.04.04 |
개발자 취준 기록 35 - 추상클래스와 인터페이스의 차이 (0) | 2024.03.30 |