IT박스

Executors.newCachedThreadPool () 및 Executors.newFixedThreadPool ()

itboxs 2020. 6. 18. 21:34
반응형

Executors.newCachedThreadPool () 및 Executors.newFixedThreadPool ()


newCachedThreadPool()newFixedThreadPool()

언제 하나를 사용해야합니까? 리소스 활용 측면에서 어떤 전략이 더 낫습니까?


문서 가이 두 기능의 차이점과 사용법을 잘 설명한다고 생각합니다.

newFixedThreadPool

공유 언 바운드 큐에서 작동하는 고정 된 수의 스레드를 재사용하는 스레드 풀을 작성합니다. 어느 시점에서나 최대 nThreads 스레드는 활성 처리 작업입니다. 모든 스레드가 활성화 될 때 추가 태스크가 제출되면 스레드가 사용 가능할 때까지 큐에서 대기합니다. 종료 전에 실행 중 실패로 인해 스레드가 종료되면 후속 작업을 실행하는 데 필요한 경우 새 스레드가 대신 사용됩니다. 풀의 스레드는 명시 적으로 종료 될 때까지 존재합니다.

newCachedThreadPool

필요에 따라 새 스레드를 작성하는 스레드 풀을 작성하지만 사용 가능한 경우 이전에 구성된 스레드를 재사용합니다. 이러한 풀은 일반적으로 많은 단기 비동기 작업을 실행하는 프로그램의 성능을 향상시킵니다. 실행 호출은 사용 가능한 경우 이전에 생성 된 스레드를 재사용합니다. 사용 가능한 기존 스레드가 없으면 새 스레드가 작성되어 풀에 추가됩니다. 60 초 동안 사용되지 않은 스레드는 종료되고 캐시에서 제거됩니다. 따라서 오랫동안 유휴 상태 인 풀은 리소스를 소비하지 않습니다. ThreadPoolExecutor 생성자를 사용하여 속성은 비슷하지만 세부 정보 (예 : 시간 초과 매개 변수)가 다른 풀을 만들 수 있습니다.

리소스 측면에서는 newFixedThreadPool모든 스레드가 명시 적으로 종료 될 때까지 계속 실행됩니다. 에서 newCachedThreadPool60 초간 사용되지 않았던 thread 종료하고 캐시에서 제거.

이 상황에서 자원 소비는 상황에 따라 크게 달라집니다. 예를 들어, 장시간 실행되는 작업이 많은 경우을 제안합니다 FixedThreadPool. 에 관해서는 CachedThreadPool, 워드 프로세서는 "이 풀은 일반적으로 단기의 비동기 태스크를 다수 실행하는 프로그램의 성능이 향상됩니다"고 말한다.


다른 답변을 완성하기 위해 Joshua Bloch의 Chapter 10, 항목 68의 Effective Java, 2nd Edition을 인용하고 싶습니다.

"특정 응용 프로그램에 대해 executor 서비스를 선택하는 것은 까다로울 수 있습니다. 작은 프로그램 이나 약간로드 된 서버를 작성하는 경우 Executors.new-CachedThreadPool을 사용 하는 것이 일반적입니다. 구성이 필요없고 일반적으로" 옳은 일." 그러나 캐시 된 스레드 풀은 좋은 선택하지 A에 대한 과도하게로드 프로덕션 서버 !

A의 캐시 된 스레드 풀 , 제출 된 작업은 대기하지 않습니다 하지만 즉시 실행 스레드에 넘겨. 사용 가능한 스레드가 없으면 새 스레드가 작성 됩니다. 서버가 너무 많이로드되어 모든 CPU가 완전히 활용되고 더 많은 작업이 도착하면 더 많은 스레드가 생성되어 문제가 더 악화됩니다.

따라서 로드 가 많은 프로덕션 서버 에서는 최대 수의 제어를 위해 스레드 수가 고정 된 풀을 제공하거나 ThreadPoolExecutor 클래스를 직접 사용하는 Executors.newFixedThreadPool 을 사용하는 것이 훨씬 좋습니다 . "


grepcode에 코드가 표시되면 ThreadPoolExecutor를 호출하는 것 입니다. 내부 및 속성 설정 요구 사항을보다 잘 제어 할 수 있도록 하나를 만들 수 있습니다.

public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

바로 그, Executors.newCachedThreadPool()여러 클라이언트와 동시 요청을 서비스의 서버 코드에 대한 좋은 선택이 아니다.

왜? 기본적으로 두 가지 (관련) 문제가 있습니다.

  1. 제한이 없습니다. 즉, 서비스에 더 많은 작업을 주입하여 (DoS 공격) 누구나 JVM을 무너 뜨릴 수있는 기회를 열어줍니다. 스레드는 무시할 수없는 양의 메모리를 소비하고 진행중인 작업에 따라 메모리 소비를 증가 시키므로 다른 회로 차단기가없는 경우 서버를 이런 방식으로 쉽게 교체 할 수 있습니다.

  2. 끝없는 문제는 집행자가 앞에 있다는 사실에 의해 악화됩니다. SynchronousQueue즉, 작업 제공자와 스레드 풀 사이에 직접적인 전달이 있음을 의미합니다. 기존의 모든 스레드가 사용중인 경우 각 새 작업은 새 스레드를 만듭니다. 이것은 일반적으로 서버 코드에 대한 나쁜 전략입니다. CPU가 포화 상태가되면 기존 작업을 완료하는 데 시간이 더 걸립니다. 더 많은 작업이 제출되고 더 많은 스레드가 생성되므로 작업을 완료하는 데 시간이 오래 걸립니다. CPU가 가득 차면 더 많은 스레드가 서버에 필요한 것은 아닙니다.

내 추천은 다음과 같습니다.

고정 크기 스레드 풀 Executors.newFixedThreadPool 또는 ThreadPoolExecutor를 사용하십시오. 설정된 최대 스레드 수;


호출 가능한 / 실행 가능한 작업 의 무제한 큐에 대해 걱정하지 않으면 그 중 하나를 사용할 수 있습니다. 브루노가 제안한 것처럼, 나는 이 둘 사이를 선호 newFixedThreadPool한다 newCachedThreadPool.

그러나 가능한 ThreadPoolExecutor는 모두에 비해 더 유연한 기능을 제공 newFixedThreadPool또는newCachedThreadPool

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

장점 :

  1. BlockingQueue 크기를 완전히 제어 할 수 있습니다 . 이전 두 옵션과 달리 제한이 없습니다. 시스템에서 예기치 않은 난기류에 보류중인 Callable / Runnable 작업이 쌓여서 메모리 부족 오류가 발생하지 않습니다.

  2. 사용자 지정 거부 처리 정책을 구현 하거나 다음 정책 중 하나를 사용할 수 있습니다 .

    1. 기본값 ThreadPoolExecutor.AbortPolicy에서 핸들러는 거부시 런타임 RejectedExecutionException을 발생시킵니다.

    2. In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.

    3. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.

    4. In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)

  3. You can implement custom Thread factory for below use cases:

    1. To set a more descriptive thread name
    2. To set thread daemon status
    3. To set thread priority

You must use newCachedThreadPool only when you have short-lived asynchronous tasks as stated in Javadoc, if you submit tasks which takes longer time to process, you will end up creating too many threads. You may hit 100% CPU if you submit long running tasks at faster rate to newCachedThreadPool (http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/).


I do some quick tests and have the following findings:

1) if using SynchronousQueue:

After the threads reach the maximum size, any new work will be rejected with the exception like below.

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@3fee733d rejected from java.util.concurrent.ThreadPoolExecutor@5acf9800[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]

at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)

2) if using LinkedBlockingQueue:

The threads never increase from minimum size to maximum size, meaning the thread pool is fixed size as the minimum size.

참고URL : https://stackoverflow.com/questions/949355/executors-newcachedthreadpool-versus-executors-newfixedthreadpool

반응형