Java Adv 22 - Executor framework

Executor ?

자바의 Excutor 프레임워크는 멀티스레딩 및 병렬처리를 쉽게 사용할 수 있도록 돕는 기능의 모음이다. 이 프레임워크는 작업 실행의 관리 및 스레드 풀 관리를 효율적으로 처리해서 개발자가 직접 스레드를 생성하고 관리하는 복잡함을 줄여준다.

image

  • pool: 스레드 풀에서 관리되는 스레드의 숫자
  • active: 작업을 수행중인 스레드의 숫자
  • queuedTask: 큐에 대기중인 작업의 숫자
  • completedTask: 완료된 작업의 숫자
16:21:25.327 [     main] == 초기 상태 == 
16:21:25.337 [     main] [pool=0, active=0, queuedTasks=0, completedTask=0]
16:21:25.338 [     main] == 작업 수행 중 == 
16:21:25.338 [     main] [pool=2, active=2, queuedTasks=2, completedTask=0]
16:21:25.338 [pool-1-thread-2] taskB 시작
16:21:25.338 [pool-1-thread-1] taskA 시작
16:21:26.343 [pool-1-thread-2] taskB 완료
16:21:26.343 [pool-1-thread-1] taskA 완료
16:21:26.344 [pool-1-thread-2] taskC 시작
16:21:26.344 [pool-1-thread-1] taskD 시작
16:21:27.348 [pool-1-thread-2] taskC 완료
16:21:27.349 [pool-1-thread-1] taskD 완료
16:21:28.344 [     main] == 작업 수행 완료 == 
16:21:28.345 [     main] [pool=2, active=0, queuedTasks=0, completedTask=4]
16:21:28.347 [     main] == shutdown == 
16:21:28.347 [     main] [pool=0, active=0, queuedTasks=0, completedTask=4]

ThreadPoolExecutor의 구성 요소 및 동작 원리

1. 구성 요소

  • 스레드 풀: 스레드를 생성하고 관리하는 역할을 한다.
  • BlockingQueue: 작업을 보관하며, 생산자-소비자 문제를 해결하기 위해 단순 큐 대신 사용된다.

2. 생산자와 소비자

  • 생산자: main 스레드가 생산자로, es.execute(작업) 호출 시 작업이 BlockingQueue에 저장된다.
  • 소비자: 스레드 풀 내의 스레드가 소비자로, BlockingQueue에서 작업을 꺼내어 처리한다.

3. ThreadPoolExecutor 생성자 주요 속성

  • corePoolSize: 기본적으로 유지되는 스레드 수.
  • maximumPoolSize: 허용되는 최대 스레드 수.
  • keepAliveTime 및 TimeUnit unit: 기본 스레드 수를 초과한 스레드가 유지되는 시간. 시간이 초과되면 스레드가 제거된다.
  • BlockingQueue workQueue: 작업을 저장하는 블로킹 큐.

4. 구체적인 예

new ThreadPoolExecutor(2, 2, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
  • corePoolSize=2, maximumPoolSize=2: 스레드 풀에서 스레드 수는 고정(2개)이다.
  • keepAliveTime=0 및 TimeUnit.MILLISECONDS: 초과 스레드가 제거될 시간이 없으므로 의미 없음.
  • LinkedBlockingQueue: 작업을 무한정 저장할 수 있는 블로킹 큐를 사용.

참고로 당연한 이야기지만 main 스레드는 작업을 전달하고 기다리지 않는다. 전달한 작업은 다른 스레드 가 실행할 것이다. main 스레드는 작업을 큐에 보관까지만 하고 바로 다음 코드를 수행한다. taskA~D 요청이 블로킹 큐에 들어온다. 최초의 작업이 들어오면 이때 작업을 처리하기 위해 스레드를 만든다. 참고로 스레드 풀에 스레드를 미리 만들어두지는 않는다. 작업이 들어올 때 마다 corePoolSize 의 크기 까지 스레드를 만든다. 예를 들어서 최초 작업인 taskA 가 들어오는 시점에 스레드1을 생성하고, 다음 작업인 taskB 가 들어오는 시점에 스레드2를 생성한다. 이런 방식으로 corePoolSize 에 지정한 수 만큼 스레드를 스레드 풀에 만든다. 여기서는 2를 설정했으므로 2개까지 만든다. corePoolSize 까지 스레드가 생성되고 나면, 이후에는 스레드를 생성하지 않고 앞서 만든 스레드를 재b사용한다.

Reference

김영한님의 자바 강의

https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html



© 2022. by taewoo

Powered by taewoo