Thread Pool

김동헌·2024년 4월 10일
0

Java

목록 보기
2/4
post-thumbnail

! 미리 알아갈 용어 !
Process : 운영체제에서 하나의 어플리케이션
Thread : process에서 하나의 작업


동시성, 병렬성

동시성(Concurrency)싱글 코어에서 멀티 스레드를 동작시키기 위한 방식입니다. 실제로 여러 작업이 동시에 이루어지는 것처럼 보이지만, 실제로는 한 시점에 하나의 스레드만 실행되는 방식으로 처리됩니다.

  • 각 스레드가 번갈아 가며 조금씩 실행되기 때문에, 사용자 입장에서는 여러 작업이 동시에 처리되는 것처럼 느끼졀 수 있습니다. 이를 통해 효율적인 자원 사용과 빠른 응답 시간을 가능하게 하여, 프로그램의 성능을 향상시킬 수 있습니다.

병렬성(Parallelism)멀티 코어에서 멀티 스레드를 동작시키는 방식으로, 각각의 코어는 독립적으로 하나 이상의 스레드를 실행할 수 있으며, 이를 통해 처리 속도를 크게 향상시킬 수 있습니다.

  • 병렬성은 데이터 병렬성(Data parallelism)작업 병렬성(Task parallelism)으로 구분된다.

데이터 병렬성

  • 데이터 병렬성은 전체 데이터를 여러 서브 데이터로 분할하고, 이를 멀티 코어 프로세서의 각 코어에서 동시에 처리하여 전체 작업의 처리 속도를 향상시키는 방법입니다.
    여기서 각 서브 데이터는 독립적으로 처리되므로, 데이터 처리의 효율성이 높아집니다.

작업 병렬성

  • 작업 병렬성(Task Parallelism)은 여러 작업을 동시에 처리하는 방식으로, 서로 다른 작업이 각각의 스레드나 프로세서에서 독립적으로 실행되어 전체적인 처리 속도를 향상시킵니다.
    • 대표적인 예 웹 서버
      웹 서버는 동시에 많은 사용자의 요청을 처리해야 하는데, 각각의 사용자 요청을 별도의 스레드에서 처리함으로써 요청 간의 간섭 없이 효율적으로 작업을 병렬로 수행할 수 있습니다.

Thread Pool

웹 애플리케이션의 경우, HTTP 요청의 수신과 처리는 톰캣과 같은 서블릿 컨테이너에 의해 이루어집니다. 톰캣은 요청을 처리하기 위해 내부적으로 쓰레드 풀을 사용하고, 이 쓰레드 풀에서 쓰레드들은 미리 생성되어 있으며, 각 HTTP 요청이 들어올 때마다 풀에서 쓰레드 하나가 할당되어 요청을 처리합니다.

그렇다면 Thread를 계속 늘려가는 건 좋을까요 ?

  • 아뇨! 하드웨어(CPU, Memory 등)의 제한적인 사항으로 Thread는 관리해야합니다. 병렬 작업 처리가 많아지면 스레드 개수가 증가하고, 그에 따른 스레드 생성과 스케줄링으로 인해 CPU의 메모리 사용량이 늘어납니다. 이는 즉, 애플리케이션의 성능 저하로 이어집니다.
  • 무분별한 스레드의 사용으로 병렬 작업의 폭증을 막기 위해 스레드 풀을 사용하는 것이 좋습니다.
    • 스레드 풀은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해 놓고 작업 큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리합니다.
    • 작업 처리가 끝난 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리합니다.
  • 따라서 작업 처리 요청이 폭증해도 작업들은 작업 큐에서 대기하다, 여유가 있는 스레드가 그것을 처리하므로 스레드의 전체 개수는 일정하며 애플리케이션의 성능도 저하되지 않습니다.

간단히, Thread Pool작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고, 작업 큐(Queue)에 들어오는 작업들을 하나씩 Thread가 맡아 처리하는 것을 의미합니다.

Java17의 경우, 멀티 스레딩을 관리하기 위해 주로 ExecutorService 인터페이스와 구현체인 ThreadPoolExecutor를 사용해 효율적으로 스레드 풀을 생성하고 관리할 수 있습니다.

Single Thread Poll

ExecutorService executorService = Executors.newSingleThreadExecutor();
  • 단일 Worker Thread
  • 실패 시, 새로운 Thread를 생성하지 않습니다.

Fixed Thread Pool

ExecutorService executorService = Executors.newFixedThreadPool(int nThreads); //고정 크기 스레드 풀
  • 고정된 Worker Thread를 사용하며, 모든 Thread를 사용 중이라면 Queue에 쌓여 대기한다.
  • 실패 시, 새로운 Thread를 생성하여 대체한다.

Cached Thread Pool

ExecutorService executorService = Executors.newCachedThreadPool(); //자동 스레드 재생 기능을 갖춘 무제한 스레드 풀
  • 필요에 따라서 새로운 Thread를 생성하며, 이전에 생성된 Thread가 존재하면 재사용합니다.
  • 기본적으로 60초 정도 Thread가 유지합니다.
  • 비동기 작업에 사용하는데 합리적입니다.

Scheduler Thread Pool

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(int corePoolSize);
  • 지정된 지연후에 명령의 실행을 예약하거나, 주기적으로 실행할 수 있는 스레드 풀을 작성합니다.

Work Stealing Thread Pool

ExecutorService executorService = Executors.newWorkStealingPool(int parallelism);
  • 지정된 parallelism을 지원할만큼 충분한 Thread를 유지하고 여러 Queue을 사용하여 경합을 줄입니다.
  • Thread를 동적으로 늘리고 줄입니다.
  • 작업이 실행되는 순서를 보장하지 않습니다.

<참고 자료>
java17 공식문서
허원철의 개발 블로그
제이온

profile
백엔드 기록 공간😁

0개의 댓글