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


93

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

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

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

답변:


108

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

더 자세히 설명하기 위해 Executors.newCachedThreadPool 및 Executors.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

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

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.