FixedThreadPool 대 CachedThreadPool : 두 가지 악 중 적은 것


93

여러 작업을 수행하는 스레드 (~ 5-150)를 생성하는 프로그램이 있습니다. 원래는 이 유사한 질문 이 더 오래 지속되는 작업에 더 적합하고 멀티 스레딩에 대한 매우 제한된 지식으로 인해 스레드의 평균 수명 (몇 분) " 장수 "를 고려 FixedThreadPool했기 때문에 a를 사용했습니다 .

그러나 최근에 추가 스레드를 생성하는 기능을 추가하여 설정 한 스레드 제한을 초과하게되었습니다. 이 경우 허용 할 수있는 스레드 수를 추측하고 늘리거나 CachedThreadPool낭비되는 스레드가 없도록 전환하는 것이 더 낫 습니까?

두 가지를 미리 시도해 보면 차이 가없는 것 같아서CachedThreadPool 낭비를 피하기 위해 함께 가려고 합니다. 그러나 스레드의 수명은 대신 a를 선택 FixedThreadPool하고 사용하지 않는 스레드를 처리 해야 함을 의미 합니까? 이 질문 은 여분의 스레드가 낭비되지 않는 것처럼 보이지만 설명에 감사드립니다.

답변:


108

CachedThreadPool은 장기 실행 스레드에 대해 사용하는 데 부정적인 결과가 없으므로 상황에 맞게 사용해야합니다. 짧은 작업에 적합한 CachedThreadPools에 대한 Java 문서의 주석은 단순히 이러한 경우에 특히 적합하다는 것을 암시하며, 장기 실행 작업과 관련된 작업에 사용할 수 없거나 사용해서는 안된다는 의미가 아닙니다.

더 자세히 설명하기 위해 Executors.newCachedThreadPoolExecutors.newFixedThreadPool 은 모두 다른 매개 변수를 사용하는 동일한 스레드 풀 구현 (적어도 개방형 JDK에서)에 의해 지원됩니다. 차이점은 스레드 최소, 최대, 스레드 종료 시간 및 큐 유형입니다.

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>());
}

FixedThreadPool은 실제로 고정 된 수의 스레드로 작업하고 싶을 때 장점이 있습니다. 그런 다음 스레드 수가 지정된 수준으로 유지 될 것임을 알면서 실행 프로그램 서비스에 원하는 수의 작업을 제출할 수 있기 때문입니다. 스레드 수를 명시 적으로 늘리려면 이것은 적절한 선택이 아닙니다.

그러나 이것은 CachedThreadPool에서 발생할 수있는 한 가지 문제는 동시에 실행되는 스레드 수를 제한하는 것과 관련이 있음을 의미합니다. CachedThreadPool은이를 제한하지 않으므로 너무 많은 스레드를 실행하지 않도록 자체 코드를 작성해야 할 수 있습니다. 이것은 실제로 응용 프로그램의 디자인과 실행 프로그램 서비스에 작업을 제출하는 방법에 따라 다릅니다.


1
"CachedThreadPool은 장기 실행 스레드에 대해 사용하는 데 부정적인 결과가 없기 때문에 사용자의 상황에 정확히 사용해야합니다." 동의하지 않는 것 같습니다. CachedThreadPool은 상한이없는 스레드를 동적으로 생성합니다. 많은 수의 스레드에서 오래 실행되는 작업은 잠재적으로 모든 리소스를 소모 할 수 있습니다. 또한 이상적인 스레드보다 많은 스레드를 사용하면 이러한 스레드의 컨텍스트 전환에 너무 많은 리소스가 낭비 될 수 있습니다. 답변의 끝 부분에서 사용자 지정 제한이 필요하다고 설명했지만 답변의 시작 부분은 약간 오해의 소지가 있습니다.
Nishit

1
왜 단순히 경계 작성 ThreadPoolExecutor등이 ThreadPoolExecutor(0, maximumPoolSize, 60L, TimeUnit.SECONDS, SynchronousQueue())?
Abhijit Sarkar 19

45

모두 FixedThreadPoolCachedThreadPool고부하 애플리케이션에서 악이다.

CachedThreadPool 보다 위험하다 FixedThreadPool

애플리케이션이로드가 많고 낮은 지연 시간이 필요한 경우 아래의 단점으로 인해 두 옵션을 모두 제거하는 것이 좋습니다.

  1. 작업 대기열의 제한없는 특성 : 메모리 부족 또는 긴 대기 시간이 발생할 수 있습니다.
  2. 오래 실행되는 스레드는 CachedThreadPool스레드 생성에 대한 제어를 벗어납니다.

둘 다 악하다는 것을 알고 있기 때문에 덜 악은 선을 행하지 않습니다. 많은 매개 변수에 대한 세부적인 제어를 제공하는 ThreadPoolExecutor를 선호 합니다.

  1. 더 나은 제어를 위해 작업 대기열을 제한된 대기열로 설정
  2. 올바른 RejectionHandler-JDK에서 제공하는 자체 RejectionHandler 또는 기본 핸들러
  3. 작업 완료 전후에 할 일이 있으면 무시 beforeExecute(Thread, Runnable)하고afterExecute(Runnable, Throwable)
  4. 스레드 사용자 정의가 필요한 경우 ThreadFactory 재정의
  5. 런타임에 동적으로 스레드 풀 크기 제어 (관련 SE 질문 : 동적 스레드 풀 )

누군가 commonPool을 사용하기로 결정하면 어떻게됩니까?
Crosk Cool

1
@Ravindra-CachedThreadPool과 FixedThreadPool의 단점을 아름답게 설명했습니다. 이것은 동시성 패키지에 대해 깊이 이해했음을 보여줍니다.
Ayaskant

5

그래서 많은 작업을 수행하는 스레드 (~ 5-150)를 생성하는 프로그램이 있습니다.

선택한 OS 및 하드웨어에서 스레드가 실제로 처리되는 방식을 이해하고 있습니까? Java가 스레드를 OS 스레드에 매핑하는 방법, 스레드를 CPU 스레드에 매핑하는 방법 등? ONE JRE에서 150 개의 스레드를 생성하는 것은 그 아래에 대규모 CPU 코어 / 스레드가있는 경우에만 의미가 있기 때문에 요청하는 것입니다. 사용중인 OS 및 RAM에 따라 n 개 이상의 스레드를 생성하면 OOM 오류로 인해 JRE가 종료 될 수도 있습니다. 따라서 스레드와 해당 스레드에서 수행 할 작업, 처리 할 수있는 작업 수 등을 실제로 구분해야합니다.

이것이 바로 CachedThreadPool의 문제입니다. 실제로 실행할 수없는 스레드에서 오래 실행되는 작업을 대기열에 넣는 것은이 스레드를 처리 할 수있는 CPU 코어가 2 개뿐이기 때문에 이치에 맞지 않습니다. 150 개의 예약 된 스레드가있는 경우 Java 및 OS 내에서 동시에 처리하는 데 사용되는 스케줄러에 불필요한 오버 헤드가 많이 발생할 수 있습니다. 스레드가 항상 I / O를 기다리지 않는 한 2 개의 CPU 코어 만 있으면 불가능합니다. 하지만이 경우에도 많은 스레드가 많은 I / O를 생성합니다.

그리고이 문제는 예를 들어 2 + n 스레드로 생성 된 FixedThreadPool에서는 발생하지 않습니다. 여기서 n은 당연히 합리적으로 낮습니다. 그 하드웨어와 OS 리소스는 어쨌든 실행할 수없는 스레드를 관리하는 데 훨씬 적은 오버 헤드로 사용되기 때문입니다.


때로는 더 나은 선택이 없습니다. CPU 코어 1 개만 가질 수 있지만 모든 사용자 요청이 요청을 처리하기 위해 스레드를 트리거하는 서버를 실행하는 경우 특히 계획하는 경우 다른 합리적인 선택이 없습니다. 사용자 기반을 늘리면 서버를 확장 할 수 있습니다.
Michel Feinstein 2017

@mFeinstein 스레드 풀 구현을 선택할 수있는 위치에 있다면 어떻게 선택할 수 없습니까? CPU 코어가 1 개인 예제에서 더 많은 스레드를 생성하는 것은 의미가 없으며 FixedThreadPool을 사용하는 예제에 완벽하게 맞습니다. 처음에는 하나 또는 두 개의 작업자 스레드로, 나중에 코어 수에 따라 10 개 또는 15 개로 쉽게 확장됩니다.
Thorsten Schöning

2
대부분의 웹 서버 구현은 각각의 새로운 HTTP 요청에 대해 하나의 새 스레드를 생성합니다 ... 그들은 머신이 얼마나 많은 실제 코어를 가지고 있는지 신경 쓰지 않을 것입니다. 이것은 구현을 더 간단하고 쉽게 확장 할 수있게합니다. 이는 한 번만 코딩하고 배포하고, 클라우드 인스턴스가 될 수있는 머신을 변경하는 경우 다시 컴파일하고 재배포 할 필요가없는 다른 많은 디자인에 적용됩니다.
Michel Feinstein

@mFeinstein 대부분의 웹 서버는 자체적으로 요청에 대해 스레드 풀을 사용합니다. 단순히 실행할 수없는 스레드 생성이 의미가 없거나 연결에 이벤트 루프를 사용하고 나중에 풀에서 요청을 처리하기 때문입니다. 또한 문제는 올바른 스레드 풀을 선택할 수 있고 어쨌든 실행할 수없는 스레드를 생성하는 것이 여전히 의미가 없다는 것입니다. 코어에 따라 기계 당 적절한 양의 스레드로 구성된 FixedthreadPool은 잘 확장됩니다.
Thorsten Schöning

3
@ ThorstenSchöning, 2 코어 머신에 50 개의 CPU 바운드 스레드가있는 것은 도움이되지 않습니다. 2 코어 시스템에 50 개의 IO 바인딩 스레드가 있으면 매우 유용 할 수 있습니다.
Paul Draper 2017
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.