java.util.concurrent
패키지에서 간단히 작업 큐를 생성할 수 있다.
ExecutorService exec = Executors.newSingleThreadExecutor(); //생성
exec.execute(runnable); //작업 할당
exec.shutdown(); //종료
그 외에도 다양한 기능을 제공한다.
nvokeAny
메서드) 혹은 모든 태스크(nvokeAll
메서드) 가 완료되기를 기다린다.awaitTermination
메서드)ExecutorCompletionService
이용)ScheduledThreadPoolExecutor
이용)큐를 둘 이상의 스레드가 처리하게 하고 싶다면 간단히 다른정적 팩터리를 이용해 다른 종류의 실행자 서비스(스레드 풀)를 생성하면 된다.
스레드풀의 스레드 개수는 고정할 수도, 필요에 따라 늘어나거나 줄어들게 할 수도 있다. 필요한 실행자 대부분은 java.util.concurrent.Executors
의 정적 팩터리들을 이용해서 생성할 수 있다.
더 자세한 설정을 원한다면 ThreadPoolExecutor
클래스를 직접 사용해도 된다. 작은 프로그램이나 서버라면 Executors.newCachedThreadPool
이 좋은 선택이다. 무거운 서버라면 이는 좋지 못하다. CachedThreadPool
은 요청받은 태스크들이 큐에 쌓이지 않고 즉시 스레드에 위임되는데 가용 스레드가 없다면 스레드를 새로 생성한다.
서버가 아주 무겁다면 CPU 이용률이 100% 에 치닫고, 새로운 태스크가 도착하는 족족 또 다른 스레드를 생성하면서 상황을 더욱 악화시킨다. 따라서 무거운 서버라면 Executors.newFixedThreadPool
이나 ThreadPoolExecutor
를 직접 사용하는게 좋다.
태스크는 Runnable
과 Callable
이 있다. Callable
은 Runnable
과 비슷하지만 값을 반환하고 임의의 예외를 던질 수 있다. 그리고 이 태스크를 수행하는 일반적인 매커니즘이 실행자 서비스, (ExecutorService
)다. 태스크를 ExecutorService
에 맡기면 원하는 태스크 수행 정책을 선택할 수 있고 언제든지 변경할 수 있다.
자바7이 되면서 실행자 프레임워크(ExecutorService
)는 fork-join 태스크
를 지원한다. fork-join 태스크
는 fork-join pool
이라는 특별한 ExecutorService
가 실행해준다. ForkJoinTask
의 인스턴스는 작은 하위 태스크로 나뉠 수 있고 ForkJoinPool
을 구성하는 스레드들이 이 태스크를 처리하며 일을 먼저 끝낸 스레드는 다른 스레드의 남은 태스크를 가져와 대신 처리할 수도 있다. 이를 통해 CPU 처리량을 높히며 낮은 지연시간을 달성할 수 잇따.