[java] 크기 제한이있는 캐시 된 스레드 풀을 만들 수 없습니까?

캐시 가능한 스레드 풀을 생성 할 수있는 스레드 수로 제한하는 것은 불가능한 것 같습니다.

정적 Executors.newCachedThreadPool이 표준 Java 라이브러리에서 구현되는 방법은 다음과 같습니다.

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

따라서 해당 템플리트를 사용하여 고정 크기의 캐시 된 스레드 풀을 작성하십시오.

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());

이제 이것을 사용하고 3 개의 작업을 제출하면 모든 것이 정상입니다. 추가 작업을 제출하면 거부 된 실행 예외가 발생합니다.

이것을 시도 :

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());

모든 스레드가 순차적으로 실행됩니다. 즉, 스레드 풀은 작업을 처리하기 위해 둘 이상의 스레드를 만들지 않습니다.

ThreadPoolExecutor의 execute 메소드에 버그가 있습니까? 아니면 이것은 의도적입니까? 아니면 다른 방법이 있습니까?

편집 : 캐시 된 스레드 풀과 똑같은 것을 원합니다 (요청시 스레드를 생성 한 다음 타임 아웃 후에 종료). 만들 수있는 스레드 수와 한 번 추가 작업을 계속 큐에 넣을 수있는 기능에 제한이 있습니다. 스레드 제한에 도달했습니다. sjlee의 답변에 따르면 이것은 불가능합니다. ThreadPoolExecutor의 execute () 메소드를 보면 실제로 불가능합니다. ThreadPoolExecutor의 서브 클래스를 작성하고 SwingWorker와 비슷한 execute ()를 재정의해야하지만 SwingWorker가 execute ()에서 수행하는 작업은 완전한 해킹입니다.



답변

ThreadPoolExecutor에는 다음과 같은 몇 가지 주요 동작이 있으며 이러한 동작으로 문제를 설명 할 수 있습니다.

작업이 제출되면

  1. 스레드 풀이 코어 크기에 도달하지 않으면 새 스레드가 작성됩니다.
  2. 코어 크기에 도달하고 유휴 스레드가 없으면 작업을 대기열에 넣습니다.
  3. 코어 크기에 도달하면 유휴 스레드가없고 큐가 가득 차면 새 스레드를 작성합니다 (최대 크기에 도달 할 때까지).
  4. 최대 크기에 도달하면 유휴 스레드가없고 큐가 가득 차면 거부 정책이 시작됩니다.

첫 번째 예에서 SynchronousQueue의 크기는 기본적으로 0입니다. 따라서 최대 크기 (3)에 도달하면 거부 정책이 시작됩니다 (# 4).

두 번째 예에서 선택한 큐는 크기가 무제한 인 LinkedBlockingQueue입니다. 그러므로, 당신은 행동 # 2에 갇히게됩니다.

동작이 거의 완전히 결정되었으므로 캐시 된 유형 또는 고정 유형으로 실제로 많은 것을 해결할 수 없습니다.

제한된 동적 스레드 풀을 사용하려면 유한 크기의 큐와 결합 된 포지티브 코어 크기 및 최대 크기를 사용해야합니다. 예를 들어

new ThreadPoolExecutor(10, // core size
    50, // max size
    10*60, // idle timeout
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<Runnable>(20)); // queue with a size

부록 : 이것은 상당히 오래된 답변이며 JDK가 0의 코어 크기와 관련하여 동작을 변경 한 것으로 보입니다. JDK 1.6부터 코어 크기가 0이고 풀에 스레드가 없으면 ThreadPoolExecutor는 해당 작업을 실행할 스레드 따라서 코어 크기 0은 위의 규칙에 대한 예외입니다. 감사합니다 스티브 에 대한 데려 내 관심 해당합니다.


답변

내가 놓친 것이 없다면, 원래 질문에 대한 해결책은 간단합니다. 다음 코드는 원본 포스터에서 설명한대로 원하는 동작을 구현합니다. 바인딩되지 않은 큐에서 작동하기 위해 최대 5 개의 스레드를 생성하고 유휴 스레드는 60 초 후에 종료됩니다.

tp = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
tp.allowCoreThreadTimeOut(true);


답변

같은 문제가 있었다. 다른 답변으로 모든 문제를 해결할 수는 없으므로 다음을 추가하고 있습니다.

이제는 문서로 명확하게 작성되었습니다 . LinkedBlockingQueue최대 스레드 설정을 차단하지 않는 큐를 사용하면 ( ) 설정이 적용되지 않고 코어 스레드 만 사용됩니다.

그래서:

public class MyExecutor extends ThreadPoolExecutor {

    public MyExecutor() {
        super(4, 4, 5,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        allowCoreThreadTimeOut(true);
    }

    public void setThreads(int n){
        setMaximumPoolSize(Math.max(1, n));
        setCorePoolSize(Math.max(1, n));
    }

}

이 유언 집행 인은 :

  1. 무제한 큐를 사용하므로 최대 스레드 개념이 없습니다. 이러한 큐가 executor가 일반적인 정책을 따르는 경우 코어가 아닌 추가 스레드를 대량으로 만들 수 있기 때문에 이는 좋은 일입니다.

  2. 최대 크기의 큐 Integer.MAX_VALUE. 보류중인 작업 수가 초과하면 Submit()throw RejectedExecutionException됩니다 Integer.MAX_VALUE. 먼저 메모리가 부족하다는 확신이 들지 않습니다.

  3. 4 개의 핵심 스레드가 가능합니다. 유휴 코어 스레드는 5 초 동안 유휴 상태 인 경우 자동으로 종료되므로 엄격하게 주문형 스레드 setThreads()입니다.

  4. 코어 스레드의 최소 수가 1보다 작지 않아야 submit()합니다. 그렇지 않으면 모든 작업이 거부됩니다. 코어 스레드는> = max threads 여야하므로 setThreads()최대 스레드 설정도 제한되지 않지만 대기열에 최대 스레드 설정은 쓸모가 없습니다.


답변

첫 번째 예에서는 AbortPolicy이가 기본값 이므로 후속 작업이 거부 RejectedExecutionHandler됩니다. ThreadPoolExecutor에는 다음 정책이 포함되어 있으며,이 정책은 setRejectedExecutionHandler메소드 를 통해 변경할 수 있습니다 .

CallerRunsPolicy
AbortPolicy
DiscardPolicy
DiscardOldestPolicy

CallerRunsPolicy를 사용하여 캐시 된 스레드 풀을 원하는 것처럼 들립니다.


답변

여기에 대한 답변 중 어느 것도 Apache의 HTTP 클라이언트 (3.x 버전)를 사용하여 제한된 양의 HTTP 연결을 만드는 것과 관련된 내 문제를 해결하지 못했습니다. 좋은 설정을 찾는 데 몇 시간이 걸렸으므로 다음과 같이 공유합니다.

private ExecutorService executor = new ThreadPoolExecutor(5, 10, 60L,
  TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
  Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

이것은 ThreadPoolExecutor5로 시작하고 실행에 사용하는 최대 10 개의 동시 실행 스레드를 보유합니다 CallerRunsPolicy.


답변

ThreadPoolExecutor 용 Javadoc에 따라 :

corePoolSize 이상이지만 maximumPoolSize 미만의 스레드가 실행중인 경우 큐가 가득 찬 경우에만 새 스레드가 작성 됩니다 . corePoolSize와 maximumPoolSize를 동일하게 설정하여 고정 크기 스레드 풀을 만듭니다.

(Emphasis mine.)

지터의 대답은 당신이 원하는 것이지만, 다른 질문에 대답합니다. 🙂


답변

옵션이 하나 더 있습니다. 새로운 SynchronousQueue를 사용하는 대신 다른 대기열도 사용할 수 있지만 크기가 1인지 확인해야하므로 executorservice가 새 스레드를 작성해야합니다.