컴퓨터 프로그래밍에서 스레드 풀은 컴퓨터 프로그램에서 실행의 동시성을 달성하기위한 소프트웨어 디자인 패턴입니다. 종종 복제 된 작업자 또는 작업자 승무원 모델이라고도하는 스레드 풀은 감독 프로그램이 작업을 동시에 실행할 수 있도록 여러 스레드를 유지 관리합니다.
Process: 운영체제에서 하나의 어플리케이션
Thread: Process에서 하나의 작업
여러 Thread를 동시에 만들어 실행(병렬처리)할 수 있다. Java에 경우, Thread, Runnable를 이용해야 한다.
그렇다고 해서 Thread를 계속 늘려가는 건 좋은 것 일까? 당연히 아니다. 하드웨어의 제한적인 사항(CPU, Memory 등)이 있기 때문에 관리할 필요가 있다. 그래서 Thread Pool이라는 개념을 이용한다.
Thread Pool은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해 놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리하는 것을 말한다. Java에 경우, 기본적으로 Executors, ExecutorService를 이용하여 Thread Pool를 만들 수 있습니다.
프로그램 성능 저하를 방지하기 위해
매번 발생하는 작업을 병렬처리하기 위해 쓰레드를 생성/수거하는 데 따른 부담은 프로그램 전체적인 퍼포먼스를 저하시킨다. 따라서 쓰레드풀을 만들어 놓고 사용한다.
쓰레드 또한 프로세스(JVM)가 할당한 메모리를 사용한다. 즉, 쓰레드를 생성하면 JVM 메모리를 소비하게 되는 것이다. Java8과 Java11에서는 쓰레드에게 최소 16KB에서 스택의 깊이가 최대로 늘어났을 때는 1MB의 메모리를 예약할당하게 된다. 쓰레드 자체도 레지스터와 스택을 가지고, 쓰레드도 컨텍스트 스위칭이 일어나기 때문에 쓰레드 생성에 따른 메모리 할당을 무시할 수 없다.
다수의 사용자 요청을 처리하기 위해
대규모 프로젝트에서 특히 중요하다. 다수의 사용자의 요청을 수용하고, 빠르게 처리하고 대응하기 위해 쓰레드풀을 사용한다.
특히 Bottle Neck 현상이 발생하는 I/O 작업과 데이터베이스 작업에서 주로 사용된다. 쓰레드가 아무리 빠르게 생성되더라도 시스템 스케줄러에서 쓰레드의 우선순위를 매번 할당해야 하는데, 쓰레드풀을 이용하면 일정 쓰레드가 이미 생성되기 때문에 쓰레드풀에 의해 라이프 사이클이 관리되고, 쓰레드 풀에 의해 작업이 큐를 이용하게 되어 우선순위가 배분되고 처리된다.
쓰레드를 생성/수거하는데 비용이 들지 않는다.
쓰레드가 생성될 때 OS가 메모리 공간을 확보해주고 메모리를 쓰레드에게 할당해준다.
쓰레드 풀을 미리 만들어 두기 때문에 처음에 생성하는 비용은 들지만 이전의 쓰레드를 재사용할 수 있으므로 시스템 자원을 줄일 수 있고, 작업을 요청 시 이미 쓰레드가 대기 중인 상태이기 때문에 작업을 실행하는 데 딜레이가 발생하지 않는다.
thread pool에 thread를 너무 많이 생성해 두었다가 사용하지 않으면 메모리 낭비가 발생한다.
Thread pool의 단점 개선 : Fork Join Thread Pool
기존 쓰레드 풀을 개선하기 위한 방법으로 Java 7 이상의 쓰레드 풀에서 사용되고 있다. 기본적으로 큰 업무를 작은 업무로 나누어 배분해서, 일을 한 후에 일을 취합한 형태이다.
동작은 다음과 같다.
1. 작업을 하나의 큰 작업들로써 제공해준다.
2. 첫 쓰레드가 작업을 가져와 자신의 로컬 큐에 할당, 분할한다.
3. 두번째 쓰레드가 가져올 작업이 없다면, 첫 쓰레드의 큐에 있는 분할된 작업을 훔쳐간다.
4. 나머지 쓰레드도 반복