[CS/운영체제] 멀티스레드와 동시성 - 33부

황제연·2025년 8월 4일
0

CS학습

목록 보기
156/193
post-thumbnail

ThreadPoolExecutor를 알아보자!

특정 작업을 진행하는 코드를 다음과 같이 구현해보았습니다

public static void main(String[] args) throws InterruptedException{
	ExecutorService service = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

	service.execute(new RunnableTask("task1"));
	service.execute(new RunnableTask("task2"));
	service.execute(new RunnableTask("task3"));

	doTask(service);
	service.close();

}

Runnable 인터페이스를 구현한 ExecutorService의 대표적인 구현체는 바로 ThreadPoolExecutor입니다

ThreadPoolExecutor 구성요소

ThreadPoolExecutor는 크게 2가지 요소로 구성되어있습니다
1. 스레드를 관리하는 스레드풀
2. 작업을 보관하는 BlockingQueue
BlockingQueue는 생산자 소비자 문제를 해결하기 위한 단순한 큐가 아니라 BlockingQueue를 사용합니다

생산자가 service.execute(new RunnableTask("task3")); 작업을 호출하면
RunnableTask("task3") 인스턴스가 BlockingQueue에 보관됩니다
생산자는 service.execute(작업)을 호출하면 내부에서 BlokcingQueue에 작업을 보관합니다
main 스레드는 생산자가 됩니다

스레드 풀에 있는 스레드는 소비자입니다 소비자 중에 하나가 BlockingQueue에 들어있는 작업을 받아서 처리합니다

ThreadPoolExecutor 생성자

ThreadPoolExecutor 생성자는 다음과 같은 속성을 사용합니다

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

LinkedBlockingQueue

이 블로킹 큐는 작업을 무한대로 저장할 수 있습니다

실행 분석

ThreadPoolExecutor를 생성한 시점에 스레드 풀에 스레드를 미리 만들어두지 않습니다
main 스레드가 호출한 뒤 기다리지 않고 다른 작업을 진행합니다.
각 작업은 호출된 스레드가 실행할 것입니다

최초 작업이 들어오면 작업을 처리하기 위해 이때 스레드를 만들고,
corePoolSize 크기까지 스레드를 만듭니다
corePoolSize까지 스레드가 생성되고 나면 이후에는 스레드를 생성하지 않고 앞서 만든 스레드를 재사용합니다

작업이 완료되면 스레드는 WAITING 상태로 스레드 풀에 대기합니다
이후 반납한 스레드는 재사용되며, 해당 코드에서 작업이 더 늘어난다면 재사용하게 됩니다

close()를 호출하면 ThreadPoolExecutor가 종료됩니다
이때 스레드 풀에 대기하는 스레드도 함께 제거됩니다

close()는 자바 19버전부터 지원하는 메소드로 이전 버전인경우 shutdown()을 호출해야합니다

Runnable의 불편함

public interface Runnable{
	void run();
}

Runnable은 반환값이 없습니다 run() 메소드를 보면 반환값을 가지지 않습니다
즉, 스레드의 실행 결과를 직접 받을 수 없으므로 멤버변수를 활용해서 join()등으로
스레드가 종료되기 까지 기다린 다음 멤버변수를 통해 값을 받아야 합니다

또한 run() 메소드는 체크 예외를 던질 수 없습니다
체크 예외의 처리는 메소드 내부에서 처리해야 합니다

Executor 프레임워크의 해결방법

Runnable의 불편함을 Executor 프레임워크는 두가지 방법을 도입해서 해결했습니다
그것은 바로 Callable과 Future라는 인터페이스입니다!

참고

  • 김영한의 실전 자바 - 고급 1편
profile
Software Developer

0개의 댓글