멀티스레드와 동시성

Ch12. 스레드 풀과 Executor 프레임워크 - Executor 스레드 풀 관리

webmaster 2024. 10. 16. 16:11
728x90

Executor 스레드 풀 관리

ExecutorService의 기본 구현체인 ThreadPoolExecutor의 생성자는 다음 속성을 사용한다.

  • corePoolSize : 스레드 풀에서 관리되는 기본 스레드의 수
  • maximumPoolSize : 스레드 풀에서 관리되는 최대 스레드 수
  • keepAliveTime , TimeUnit unit : 기본 스레드 수를 초과해서 만들어진 초과 스레드가 생존할 수 있는 대 시간, 시간 동안 처리할 작업이 없다면 초과 스레드는 제거된다.
  • BlockingQueue workQueue : 작업을 보관할 블로킹

Executor 스레드 풀 관리 - 실행 코드
Executor 스레드 풀 관리 - 실행 결과

  • 작업을 보관할 블로킹 큐의 구현체로 ArrayBlockingQueue(2)를 사용했다. 사이즈를 2로 설정했으므로 최 대 2개까지 작업을 큐에 보관할 수 있다.
  • corePoolSize=2 , maximumPoolSize=4를 사용해서 기본 스레드는 2개, 최대 스레드는 4개로 설정했다.
    • 스레드 풀에 기본 2개의 스레드를 운영한다. 요청이 너무 많거나 급한 경우 스레드 풀은 최대 4개까지 스레 드를 증가시켜서 사용할 수 있다. 이렇게 기본 스레드 수를 초과해서 만들어진 스레드를 초과 스레드라 하겠다.
  • 3000 , TimeUnit.MILLISECONDS
    • 초과 스레드가 생존할 있는 대기 시간을 뜻한다. 시간 동안 초과 스레드가 처리할 작업이 없다면 초과 스레드는 제거된다.
    • 여기서는 3000 밀리초(3) 설정했으므로, 초과 스레드가 3초간 작업을 하지 않고 대기한다면 초과 스레드는 스레드 풀에서 제거된다.

분석

Executor 스레드 풀 관리 - 실행 결과 분석1

  • task1 작업을 요청한다.
  • Executor는 스레드 풀에 스레드가 core 사이즈만큼 있는지 확인한다.
    • core 사이즈만큼 없다면 스레드를 하나 생성한다.
    • 작업을 처리하기 위해 스레드를 하나 생성했기 때문에 작업을 큐에 넣을 필요 없이, 해당 스레드가 바로 업을 처리한다.

Executor 스레드 풀 관리 - 실행 결과 분석2
Executor 스레드 풀 관리 - 실행 결과 로그2

  • 새로 만들어진 스레드1이 task1을 수행한다.

Executor 스레드 풀 관리 - 실행 결과 분석3
Executor 스레드 풀 관리 - 실행 결과 로그3

  • task2를 요청한다.
  • Executor는 스레드 풀에 스레드가 core 사이즈만큼 있는지 확인한다.
    • 아직 core 사이즈 만큼 없으므로 스레드를 하나 생성한다.
  • 새로 만들어진 스레드2 task2를 처리한다

Executor 스레드 풀 관리 - 실행 결과 분석4

 

Executor 스레드 풀 관리 - 실행 결과 로그4

  • task3 작업을 요청한다.
  • Executor는 스레드 풀에 스레드가 core 사이즈만큼 있는지 확인한다.
  • core 사이즈만큼 스레드가 이미 만들어져 있고, 스레드 풀에 사용할 있는 스레드가 없으므로 경우 큐에 업을 보관한다.

Executor 스레드 풀 관리 - 실행 결과 분석5
Executor 스레드 풀 관리 - 실행 결과 로그5

  • task4 작업을 요청한다.
  • Executor는 스레드 풀에 스레드가 core 사이즈만큼 있는지 확인한다.
  • core 사이즈만큼 스레드가 이미 만들어져 있고, 스레드 풀에 사용할 있는 스레드가 없으므로 경우 큐에 업을 보관한다.

Executor 스레드 풀 관리 - 실행 결과 분석6
Executor 스레드 풀 관리 - 실행 결과 로그6

  • task5 작업을 요청한다.
    • Executor는 스레드 풀에 스레드가 core 사이즈만큼 있는지 확인한다. -> core 사이즈만큼 있다.
    • Executor는 큐에 보관을 시도한다 -> 큐가 가득 찼다.
  • 큐가 가득 차면 긴급 상황이다. 대기하는 작업이 정도로 요청이 많다는 뜻이다. 경우 Executor는 max(maximumPoolSize) 사이즈까지 초과 스레드 만들어서 작업을 수행한다.
    • core=2 : 기본 스레드는 최대 2개
    • max=4 :기본스레드 2개에 초과 스레드 2개 합계 총 4개 가능(초과스레드= max - core )
  • Executor는 초과 스레드인 스레드 3을 만든다.
    • 작업을 처리하기 위해 스레드를 하나 생성했기 때문에 작업을 큐에 넣을 필요 없이, 해당 스레드가 바로 작업을 처리한다.
    • 참고로 경우 큐가 가득 찼기 때문에 큐에 넣는 것도 불가능하다.
  • 스레드 3이 task5를 처리한다.

Executor 스레드 풀 관리 - 실행 결과 분석7
Executor 스레드 풀 관리 - 실행 결과 로그7

  • task6 작업을 요청한다.
  • 큐가 가득 찼다.
  • Executor는 초과 스레드인 스레드 4를 만들어서 task6을 처리한다.
    • 큐가 가득찼기 때문에 작업을 큐에 넣는 것은 불가능하다.

Executor 스레드 풀 관리 - 실행 결과 분석8
Executor 스레드 풀 관리 - 실행 결과 로그8

  • task7 작업을 요청한다.
  • 큐가 가득 찼다.
  • 스레드 풀의 스레드도 max 사이즈만큼 가득 찼다.
  • RejectedExecutionException이 발생한다.

이 경우 큐에 넣을 수도 없고, 작업을 수행할 스레드도 만들 수 없다. 따라서 작업을 거절한다.

Executor 스레드 풀 관리 - 실행 결과 분석9
Executor 스레드 풀 관리 - 실행 결과 로그9
  • 스레드 1이 task1을 스레드 3이 task5의 작업을 완료하고 스레드 풀에 대기 상태로 돌아간다.

Executor 스레드 풀 관리 - 실행 결과 분석10

  • 스레드 풀의 스레드는 큐의 데이터를 획득하기 위해 대기한다.
  • 스레드 1: task3을 획득한다.
  • 스레드 3: task4를 획득한다.

Executor 스레드 풀 관리 - 실행 결과 분석11
Executor 스레드 풀 관리 - 실행 결과 로그11

  • 모든 작업이 완료된다.

Executor 스레드 풀 관리 - 실행 결과 분석12
Executor 스레드 풀 관리 - 실행 결과 로그12

  • 스레드 3, 스레드 4와 같은 초과 스레드들은 지정된 시간까지 작업을 하지 않고 대기하면 제거된다. 긴급한 작업들 이 끝난 것으로 이해하면 된다.
  • 여기서는 지정한 3초간 스레드 3, 스레드 4가 작업을 진행하지 않았기 때문에 스레드 풀에서 제거된다.
  • 참고로 초과 스레드가 작업을 처리할 때마다 시간은 계속 초기화된다.
    • 작업 요청이 계속 들어온다면 긴급한 상황이 끝난 것이 아니다. 따라서 긴급한 상황이 끝날 때까지는 초과 스레드를 살려두는 것이 많은 스레드를 사용해서 작업을 빨리 처리할 있다.

Executor 스레드 풀 관리 - 실행 결과 분석13

  • 초과 스레드가 제거된 모습이다.=
  • 이후에 shutdown() 이 진행되면 풀의 스레드도 모두 제거된다.

스레드 미리 생성하기

응답시간이 아주 중요한 서버라면, 서버가 고객의 처음 요청을 받기 전에 스레드를 스레드 풀에 미리 생성해두고 싶을 수 있다.
스레드를 미리 생성해 두면, 처음 요청에서 사용되는 스레드의 생성 시간을 줄일 수 있다.

ThreadPoolExecutor.prestartAllCoreThreads()를 사용하면 기본 스레드를 미리 생성할 있다. 참고로 ExecutorService는 이 메서드를 제공하지 않는다.

스레드 미리 생성하기
실행 결과

728x90