Qual é exatamente a diferença entre o tamanho do pool principal e o tamanho máximo do pool quando falamos em termos de ThreadPoolExecutor
?
Isso pode ser explicado com a ajuda de um exemplo?
Qual é exatamente a diferença entre o tamanho do pool principal e o tamanho máximo do pool quando falamos em termos de ThreadPoolExecutor
?
Isso pode ser explicado com a ajuda de um exemplo?
Respostas:
A partir deste post :
Veja este exemplo. O tamanho do conjunto de encadeamentos inicial é 1, o tamanho do conjunto principal é 5, o tamanho máximo do conjunto é 10 e a fila é 100.
Conforme as solicitações chegam, até 5 threads são criados e as tarefas são adicionadas à fila até chegar a 100. Quando a fila estiver cheia, novos threads serão criados até
maxPoolSize
. Quando todos os threads estiverem em uso e a fila cheia, as tarefas serão rejeitadas. Conforme a fila diminui, também diminui o número de threads ativos.
allowCoreThreadTimeOut(boolean)
que permite que os threads principais sejam eliminados após um determinado tempo inativo. Definir isso como verdadeiro e definir core threads
= max threads
permite que o pool de threads seja dimensionado entre 0 e max threads
.
IF executando threads> corePoolSize & <maxPoolSize , crie uma nova Thread se a fila de tarefas Total estiver cheia e uma nova estiver chegando.
Documento de formulário: (se houver mais de corePoolSize, mas menos de threads maximumPoolSize em execução, um novo thread será criado apenas se a fila estiver cheia.)
Agora, pegue um exemplo simples,
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));
Aqui, 5 é o corePoolSize - significa que Jvm criará um novo thread para uma nova tarefa para as 5 primeiras tarefas. e outras tarefas serão adicionadas à fila até que a fila fique cheia (50 tarefas).
10 é o maxPoolSize - JVM pode criar no máximo 10 encadeamentos. Significa que se já houver 5 tarefas / encadeamento em execução e a fila estiver cheia com 50 tarefas pendentes e se mais uma nova solicitação / tarefa estiver chegando na fila, a JVM criará um novo encadeamento até 10 (total de encadeamentos = 5 anteriores + 5 novos) ;
new ArrayBlockingQueue (50) = é o tamanho total da fila - ele pode enfileirar 50 tarefas nela.
quando todos os 10 threads estiverem em execução e se uma nova tarefa estiver chegando, essa nova tarefa será rejeitada.
Regras para criar Threads internamente pela SUN:
Se o número de threads for menor que corePoolSize, crie um novo Thread para executar uma nova tarefa.
Se o número de threads for igual (ou maior que) corePoolSize, coloque a tarefa na fila.
Se a fila estiver cheia e o número de encadeamentos for menor que maxPoolSize, crie um novo encadeamento para executar tarefas.
Se a fila estiver cheia e o número de threads for maior ou igual a maxPoolSize, rejeite a tarefa.
Espero, isso é útil .. e por favor me corrija se eu estiver errado ...
Do doc :
Quando uma nova tarefa é enviada no método execute (java.lang.Runnable), e menos de threads corePoolSize estão em execução, um novo thread é criado para lidar com a solicitação, mesmo se outros threads de trabalho estiverem ociosos. Se houver mais de corePoolSize, mas menos de threads maximumPoolSize em execução, um novo thread será criado apenas se a fila estiver cheia.
Além disso:
Ao definir corePoolSize e maximumPoolSize iguais, você cria um pool de threads de tamanho fixo. Ao definir maximumPoolSize como um valor essencialmente ilimitado, como Integer.MAX_VALUE, você permite que o pool acomode um número arbitrário de tarefas simultâneas. Normalmente, os tamanhos de pool máximo e principal são definidos apenas na construção, mas também podem ser alterados dinamicamente usando setCorePoolSize (int) e setMaximumPoolSize (int).
Se você decidir criar um ThreadPoolExecutor
manualmente em vez de usar a Executors
classe de fábrica, precisará criar e configurar um usando um de seus construtores. O construtor mais abrangente desta classe é:
public ThreadPoolExecutor(
int corePoolSize,
int maxPoolSize,
long keepAlive,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
);
Como você pode ver, você pode configurar:
Limitar o número de tarefas simultâneas em execução, dimensionar seu pool de threads representa um grande benefício para seu aplicativo e seu ambiente de execução em termos de previsibilidade e estabilidade: uma criação de thread ilimitada acabará esgotando os recursos de tempo de execução e seu aplicativo pode experimentar como consequência , sérios problemas de desempenho que podem levar até mesmo à instabilidade do aplicativo.
Essa é uma solução para apenas uma parte do problema: você está limitando o número de tarefas sendo executadas, mas não está limitando o número de trabalhos que podem ser enviados e enfileirados para execução posterior. O aplicativo apresentará escassez de recursos posteriormente, mas eventualmente ocorrerá se a taxa de envio ultrapassar consistentemente a taxa de execução.
A solução para esse problema é: Fornecer uma fila de bloqueio ao executor para manter as tarefas em espera. No caso de a fila ficar cheia, a tarefa enviada será "rejeitada". O RejectedExecutionHandler
é chamado quando um envio de tarefa é rejeitado e é por isso que o verbo rejeitado foi citado no item anterior. Você pode implementar sua própria política de rejeição ou usar uma das políticas integradas fornecidas pela estrutura.
A política de rejeição padrão faz com que o executor lance um RejectedExecutionException
. No entanto, outras políticas integradas permitem que você:
Regras de um tamanho de pool ThreadPoolExecutor
As regras para o tamanho de uma ThreadPoolExecutor's
piscina geralmente não são compreendidas, porque ela não funciona da maneira que você acha que deveria ou da maneira que você deseja.
Veja este exemplo. O tamanho do conjunto de encadeamentos inicial é 1, o tamanho do conjunto principal é 5, o tamanho máximo do conjunto é 10 e a fila é 100.
Maneira da Sun: à medida que as solicitações chegam em threads serão criadas até 5, as tarefas serão adicionadas à fila até chegar a 100. Quando a fila estiver cheia, novos threads serão criados até maxPoolSize
. Quando todos os threads estiverem em uso e a fila cheia, as tarefas serão rejeitadas. À medida que a fila diminui, também diminui o número de threads ativos.
Forma antecipada pelo usuário: conforme as solicitações chegam em threads serão criadas até 10, as tarefas serão adicionadas à fila até chegar a 100, momento em que são rejeitadas. O número de threads será renomeado no máximo até que a fila esteja vazia. Quando a fila estiver vazia, os threads morrerão até que corePoolSize
sobrem.
A diferença é que os usuários querem começar a aumentar o tamanho do pool mais cedo e querem que a fila seja menor, enquanto o método Sun deseja manter o tamanho do pool pequeno e apenas aumentá-lo quando a carga se tornar muito.
Aqui estão as regras da Sun para a criação de threads em termos simples:
corePoolSize
, crie um novo Thread para executar uma nova tarefa.corePoolSize
, coloque a tarefa na fila.maxPoolSize
, crie um novo thread para executar tarefas.maxPoolSize
, rejeite a tarefa. Resumindo, os novos encadeamentos são criados apenas quando a fila fica cheia, portanto, se você estiver usando uma fila ilimitada, o número de encadeamentos não será superior corePoolSize
.Para uma explicação mais completa, pegue na boca dos cavalos: ThreadPoolExecutor
documentação da API.
Há uma postagem de fórum muito boa que mostra como ThreadPoolExecutor
funciona com exemplos de código: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0
Mais informações: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450
Você pode encontrar a definição dos termos corepoolsize e maxpoolsize no javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
O link acima tem a resposta para sua pergunta. No entanto, só para deixar isso claro. O aplicativo continuará criando threads até atingir corePoolSize. Acho que a ideia aqui é que esses muitos threads devem ser suficientes para lidar com o fluxo de entrada de tarefas. Se uma nova tarefa vier após os threads corePoolSize serem criados, as tarefas serão enfileiradas. Quando a fila estiver cheia, o executor começará a criar novos threads. É uma espécie de equilíbrio. O que essencialmente significa é que o influxo de tarefas é mais do que a capacidade de processamento. Portanto, o Executor começará a criar novos threads novamente até atingir o número máximo de threads. Novamente, um novo encadeamento será criado se e somente se a fila estiver cheia.
Boa explicação neste blog:
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;
}
}
}
}
}
Resultado :
---- 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
Do livro Java Concurency Essentials :
CorePoolSize : O ThreadPoolExecutor tem um atributo corePoolSize que determina quantos threads ele iniciará até que novos threads sejam iniciados apenas quando a fila estiver cheia
MaximumPoolSize : este atributo determina quantos threads são iniciados no máximo. Você pode definir isso como Inteiro. MAX_VALUE para não ter limite superior
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);
}
Entender o comportamento interno de ThreadPoolExecutor
quando uma nova tarefa é enviada me ajudou a entender como corePoolSize
e como maximumPoolSize
diferir.
Deixei:
N
ser o número de threads no pool getPoolSize()
,. Threads ativos + threads inativos.T
ser a quantidade de tarefas submetidas ao executor / pool.C
ser o tamanho do pool principal getCorePoolSize()
. Quantos threads podem ser criados por pool para as tarefas de entrada antes que novas tarefas vão para a fila .M
ser o tamanho máximo do pool getMaximumPoolSize()
. Quantidade máxima de threads que o pool pode alocar.Comportamentos do ThreadPoolExecutor
em Java quando uma nova tarefa é enviada:
N <= C
, os threads ociosos não são atribuídos à nova tarefa de entrada, em vez disso, um novo thread é criado.N > C
e se houver threads inativos, uma nova tarefa é atribuída a eles.N > C
e se não houver encadeamentos ociosos, novas tarefas são colocadas na fila. NENHUMA NOVA LINHA CRIADA AQUI.M
. Se M
for alcançado, rejeitamos as tarefas. O que é importante não notar aqui é que não criamos novos threads até que a fila esteja cheia!Fontes:
corePoolSize = 0
e maximumPoolSize = 10
com capacidade de fila de 50
.Isso resultará em um único encadeamento ativo no pool até que a fila contenha 50 itens.
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
e maximumPoolSize = 10
com capacidade de fila de 50
.Isso resultará em 10 threads ativos no pool. Quando a fila tiver 50 itens, as tarefas serão rejeitadas.
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`.