우리가 이야기 할 때 코어 풀 크기 와 최대 풀 크기 의 차이점은 정확히 무엇입니까 ThreadPoolExecutor
?
예를 들어 설명 할 수 있습니까?
우리가 이야기 할 때 코어 풀 크기 와 최대 풀 크기 의 차이점은 정확히 무엇입니까 ThreadPoolExecutor
?
예를 들어 설명 할 수 있습니까?
답변:
에서 이 블로그 게시물 :
이 예를 들어 보자. 시작 스레드 풀 크기는 1, 코어 풀 크기는 5, 최대 풀 크기는 10, 대기열은 100입니다.
요청이 들어 오면 스레드는 최대 5 개까지 생성되고 작업은 100 개에 도달 할 때까지 대기열에 추가됩니다. 대기열이 가득 차면 최대
maxPoolSize
. 모든 스레드가 사용 중이고 대기열이 가득 차면 작업이 거부됩니다. 대기열이 줄어들면 활성 스레드 수도 감소합니다.
allowCoreThreadTimeOut(boolean)
주어진 유휴 시간 후에 코어 스레드를 종료 할 수 있는 흥미로운 방법 이 있습니다. 이것을 true로 설정하고 core threads
=로 설정 max threads
하면 스레드 풀이 0과 max threads
.
IF 실행 스레드> corePoolSize를 & <maxPoolSize 총 작업 대기열이 가득하고 새로운 하나가 도착하면, 새로운 스레드를 생성합니다.
Form doc : ( corePoolSize 이상 이지만 maximumPoolSize 미만의 스레드가 실행중인 경우 대기열이 가득 찬 경우에만 새 스레드가 생성됩니다.)
이제 간단한 예를 들어 보겠습니다.
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));
여기서 5는 corePoolSize입니다 . 즉, Jvm이 처음 5 개 작업에 대한 새 작업에 대한 새 스레드를 생성합니다. 다른 작업은 대기열이 가득 찰 때까지 대기열에 추가됩니다 (작업 50 개).
10은 maxPoolSize입니다 . JVM은 최대 10 개의 스레드를 만들 수 있습니다. 이미 5 개의 작업 / 스레드가 실행 중이고 대기열이 50 개의 보류 작업으로 가득 차 있고 하나 이상의 새 요청 / 작업이 대기열에 도착하면 JVM은 최대 10 개의 새 스레드를 생성합니다 (총 스레드 = 이전 5 개 + 새 5 개). ;
new ArrayBlockingQueue (50) = 전체 대기열 크기-50 개의 작업을 대기열에 넣을 수 있습니다.
10 개의 스레드가 모두 실행되고 새 작업이 도착하면 새 작업이 거부됩니다.
SUN에서 내부적으로 스레드를 생성하는 규칙 :
스레드 수가 corePoolSize보다 적 으면 새 스레드를 만들어 새 작업을 실행합니다.
스레드 수가 corePoolSize와 같거나 크면 작업을 대기열에 넣습니다.
대기열이 가득 차고 스레드 수가 maxPoolSize보다 적 으면 작업을 실행할 새 스레드를 만듭니다.
대기열이 가득 차고 스레드 수가 maxPoolSize보다 크거나 같으면 작업을 거부합니다.
희망, 이것은 HelpFul .. 그리고 내가 틀렸다면 나를 고쳐주세요 ...
에서 의 DoC :
새 작업이 execute (java.lang.Runnable) 메소드에 제출되고 corePoolSize 스레드보다 적은 수의 스레드가 실행 중이면 다른 작업자 스레드가 유휴 상태 인 경우에도 요청을 처리하기 위해 새 스레드가 생성됩니다. corePoolSize보다 많지만 maximumPoolSize보다 작은 스레드가 실행중인 경우 대기열이 가득 찬 경우에만 새 스레드가 생성됩니다.
더욱이:
corePoolSize 및 maximumPoolSize를 동일하게 설정하여 고정 크기 스레드 풀을 만듭니다. maximumPoolSize를 Integer.MAX_VALUE와 같이 본질적으로 제한되지 않은 값으로 설정하면 풀이 임의의 수의 동시 작업을 수용 할 수 있습니다. 대부분의 경우 코어 및 최대 풀 크기는 생성시에만 설정되지만 setCorePoolSize (int) 및 setMaximumPoolSize (int)를 사용하여 동적으로 변경할 수도 있습니다.
팩토리 클래스 ThreadPoolExecutor
를 사용하는 대신 수동으로 생성하기로 결정한 경우 Executors
생성자 중 하나를 사용하여 생성하고 구성해야합니다. 이 클래스의 가장 광범위한 생성자는 다음과 같습니다.
public ThreadPoolExecutor(
int corePoolSize,
int maxPoolSize,
long keepAlive,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
);
보시다시피 다음을 구성 할 수 있습니다.
실행중인 동시 작업 수를 제한하고 스레드 풀 크기를 조정하는 것은 예측 가능성 및 안정성 측면에서 애플리케이션 및 실행 환경에 큰 이점을 나타냅니다. 제한없는 스레드 생성은 결국 런타임 리소스를 고갈시키고 결과적으로 애플리케이션이 경험할 수 있습니다. , 응용 프로그램을 불안정하게 만들 수도있는 심각한 성능 문제.
이는 문제의 한 부분에 대한 해결책입니다. 실행중인 작업의 수를 제한하고 있지만 나중에 실행하기 위해 제출하고 대기열에 넣을 수있는 작업의 수를 제한하지 않습니다. 응용 프로그램은 나중에 리소스 부족을 경험하지만 제출 속도가 지속적으로 실행 속도를 초과하면 결국이를 경험하게됩니다.
이 문제에 대한 해결책은 다음과 같습니다. 대기중인 작업을 유지하기 위해 실행기에 차단 대기열을 제공합니다. 대기열이 꽉 찬 경우 제출 된 작업은 "거부"됩니다. 는 RejectedExecutionHandler
작업 제출이 거부 될 때 호출되며, 거부 동사가 이전 항목에서 인용 한 이유의 그. 자체 거부 정책을 구현하거나 프레임 워크에서 제공하는 기본 제공 정책 중 하나를 사용할 수 있습니다.
기본 거부 정책에는 실행자가 RejectedExecutionException
. 그러나 다른 기본 제공 정책을 통해 다음을 수행 할 수 있습니다.
ThreadPoolExecutor 풀 크기 규칙
ThreadPoolExecutor's
풀 의 크기에 대한 규칙 은 일반적으로 잘못 이해되어 있습니다. 왜냐하면 풀이 필요하다고 생각하는 방식이나 원하는 방식으로 작동하지 않기 때문입니다.
이 예를 들어 보자. 시작 스레드 풀 크기는 1, 코어 풀 크기는 5, 최대 풀 크기는 10, 대기열은 100입니다.
썬의 방식 : 요청이 들어 오면 스레드가 최대 5 개까지 생성 된 다음 작업이 100 개에 도달 할 때까지 대기열에 추가됩니다. 대기열이 가득 차면 최대 새 스레드가 생성됩니다. maxPoolSize
. 모든 스레드가 사용 중이고 대기열이 가득 차면 작업이 거부됩니다. 큐가 줄어들면 활성 스레드 수가 줄어 듭니다.
사용자 예상 방식 : 요청이 들어 오면 스레드가 최대 10 개까지 생성 된 다음 작업이 100 개에 도달 할 때까지 대기열에 추가되고 거부되는 시점입니다. 스레드 수는 대기열이 비워 질 때까지 최대로 이름이 바뀝니다. 대기열이 비어 있으면 스레드가 corePoolSize
남아 있을 때까지 죽습니다 .
차이점은 사용자가 더 일찍 풀 크기를 늘리기 시작하고 대기열을 더 작게 만들고 싶어한다는 것입니다. Sun 방법은 풀 크기를 작게 유지하고로드가 많아지면 늘려야합니다.
다음은 스레드 생성에 대한 Sun의 규칙입니다.
corePoolSize
새 스레드를 만들어 새 작업을 실행합니다.corePoolSize
작업을 대기열에 넣습니다.maxPoolSize
작업을 실행할 새 스레드를 만듭니다.maxPoolSize
이면 작업을 거부합니다. 길고 짧은 것은 대기열이 가득 찼을 때만 새 스레드가 생성되므로 제한되지 않은 대기열을 사용하는 경우 스레드 수가을 초과하지 않는다는 것 corePoolSize
입니다.더 자세한 설명은 말의 입에서 가져옵니다 : ThreadPoolExecutor
API 문서.
ThreadPoolExecutor
코드 예제와 함께 작동 하는 방법을 설명하는 정말 좋은 포럼 게시물이 있습니다 . http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0
추가 정보 : http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450
javadoc에서 corepoolsize 및 maxpoolsize 용어의 정의를 찾을 수 있습니다. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
위의 링크에 귀하의 질문에 대한 답변이 있습니다. 그러나 명확하게하기 위해서입니다. 응용 프로그램은 corePoolSize에 도달 할 때까지 스레드를 계속 생성합니다. 여기서 생각은 이러한 많은 스레드가 작업 유입을 처리하기에 충분해야한다는 것입니다. corePoolSize 스레드가 생성 된 후 새 작업이 발생하면 작업이 대기열에 추가됩니다. 큐가 가득 차면 실행기는 새 스레드를 만들기 시작합니다. 일종의 균형입니다. 본질적으로 의미하는 것은 작업의 유입이 처리 능력 이상이라는 것입니다. 따라서 Executor는 최대 스레드 수에 도달 할 때까지 새 스레드를 다시 만들기 시작합니다. 다시 말하지만, 대기열이 가득 찬 경우에만 새 스레드가 생성됩니다.
이 블로그의 좋은 설명 :
public class ThreadPoolExecutorExample {
public static void main (String[] args) {
createAndRunPoolForQueue(new ArrayBlockingQueue<Runnable>(3), "Bounded");
createAndRunPoolForQueue(new LinkedBlockingDeque<>(), "Unbounded");
createAndRunPoolForQueue(new SynchronousQueue<Runnable>(), "Direct hand-off");
}
private static void createAndRunPoolForQueue (BlockingQueue<Runnable> queue,
String msg) {
System.out.println("---- " + msg + " queue instance = " +
queue.getClass()+ " -------------");
ThreadPoolExecutor e = new ThreadPoolExecutor(2, 5, Long.MAX_VALUE,
TimeUnit.NANOSECONDS, queue);
for (int i = 0; i < 10; i++) {
try {
e.execute(new Task());
} catch (RejectedExecutionException ex) {
System.out.println("Task rejected = " + (i + 1));
}
printStatus(i + 1, e);
}
e.shutdownNow();
System.out.println("--------------------\n");
}
private static void printStatus (int taskSubmitted, ThreadPoolExecutor e) {
StringBuilder s = new StringBuilder();
s.append("poolSize = ")
.append(e.getPoolSize())
.append(", corePoolSize = ")
.append(e.getCorePoolSize())
.append(", queueSize = ")
.append(e.getQueue()
.size())
.append(", queueRemainingCapacity = ")
.append(e.getQueue()
.remainingCapacity())
.append(", maximumPoolSize = ")
.append(e.getMaximumPoolSize())
.append(", totalTasksSubmitted = ")
.append(taskSubmitted);
System.out.println(s.toString());
}
private static class Task implements Runnable {
@Override
public void run () {
while (true) {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
break;
}
}
}
}
}
출력 :
---- Bounded queue instance = class java.util.concurrent.ArrayBlockingQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueCapacity = 1, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 3, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 4, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Unbounded queue instance = class java.util.concurrent.LinkedBlockingDeque -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2147483646, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueRemainingCapacity = 2147483645, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 2147483644, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 2, corePoolSize = 2, queueSize = 4, queueRemainingCapacity = 2147483643, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 2, corePoolSize = 2, queueSize = 5, queueRemainingCapacity = 2147483642, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 2, corePoolSize = 2, queueSize = 6, queueRemainingCapacity = 2147483641, maximumPoolSize = 5, totalTasksSubmitted = 8
poolSize = 2, corePoolSize = 2, queueSize = 7, queueRemainingCapacity = 2147483640, maximumPoolSize = 5, totalTasksSubmitted = 9
poolSize = 2, corePoolSize = 2, queueSize = 8, queueRemainingCapacity = 2147483639, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Direct hand-off queue instance = class java.util.concurrent.SynchronousQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 3, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 4, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
Task rejected = 6
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
Task rejected = 7
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
Task rejected = 8
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
Process finished with exit code 0
책 Java 동시성 에센셜에서 :
CorePoolSize : ThreadPoolExecutor에는 대기열이 가득 찼을 때만 새 스레드가 시작될 때까지 시작할 스레드 수를 결정하는 corePoolSize 속성이 있습니다.
MaximumPoolSize :이 속성은 최대로 시작되는 스레드 수를 결정합니다. 이것을 Integer로 설정할 수 있습니다. 상한이없는 MAX_VALUE
java.util.concurrent.ThreadPoolExecutor
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
ThreadPoolExecutor
새 작업이 제출 될 때 의 내부 동작을 이해하면 어떻게 corePoolSize
그리고 maximumPoolSize
다른지 이해하는 데 도움이되었습니다 .
허락하다:
N
풀에있는 스레드 수, getPoolSize()
. 활성 스레드 + 유휴 스레드.T
실행자 / 풀에 제출 된 작업의 양입니다.C
코어 풀 크기, getCorePoolSize()
. 새 작업이 대기열로 이동하기 전에 들어오는 작업에 대해 풀당 최대 몇 개의 스레드를 만들 수 있습니다 .M
최대 풀 크기 getMaximumPoolSize()
입니다. 풀이 할당 할 수있는 최대 스레드 수입니다.ThreadPoolExecutor
새 작업이 제출 될 때 Java의 동작 :
N <= C
유휴 스레드에 새 수신 작업이 할당되지 않고 대신 새 스레드가 생성됩니다.N > C
와 경우 유휴 스레드가 새 작업이이 할당 된 후이 있습니다.N > C
및 유휴 스레드가없는 경우, 새로운 작업 큐에 배치됩니다. 여기에 새로운 스레드가 생성되지 않았습니다.M
. 경우 M
에 도달 할 때, 우리는 작업을 거부합니다. 여기서 중요한 것은 대기열이 가득 찰 때까지 새 스레드를 생성하지 않는다는 것입니다!출처 :
corePoolSize = 0
및 maximumPoolSize = 10
의 큐 용량 50
.그러면 대기열에 50 개의 항목이있을 때까지 풀에 단일 활성 스레드가 생성됩니다.
executor.execute(task #1):
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #1 immediately queued and kicked in b/c the very first thread is created when `workerCountOf(recheck) == 0`]
execute(task #2):
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #2 not starting before #1 is done]
... executed a few tasks...
execute(task #19)
before task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 17, completed tasks = 0]
after task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 18, completed tasks = 0]
...
execute(task #51)
before task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 50, completed tasks = 0]
after task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 2, active threads = 2, queued tasks = 50, completed tasks = 0]
Queue is full.
A new thread was created as the queue was full.
corePoolSize = 10
및 maximumPoolSize = 10
의 큐 용량 50
.그러면 풀에서 10 개의 활성 스레드가 생성됩니다. 대기열에 50 개의 항목이 있으면 작업이 거부됩니다.
execute(task #1)
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
execute(task #2)
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
execute(task #3)
before task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
after task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
... executed a few tasks...
execute(task #11)
before task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]
after task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 1, completed tasks = 0]
... executed a few tasks...
execute(task #51)
before task #51 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 50, completed tasks = 0]
Task was rejected as we have reached `maximumPoolSize`.