newCachedThreadPool()
versus newFixedThreadPool()
Quando devo usar um ou outro? Qual estratégia é melhor em termos de utilização de recursos?
newCachedThreadPool()
versus newFixedThreadPool()
Quando devo usar um ou outro? Qual estratégia é melhor em termos de utilização de recursos?
Acho que os documentos explicam muito bem a diferença e o uso dessas duas funções:
Cria um conjunto de encadeamentos que reutiliza um número fixo de encadeamentos que operam em uma fila ilimitada compartilhada. A qualquer momento, no máximo os nThreads threads serão tarefas de processamento ativas. Se tarefas adicionais forem enviadas quando todos os encadeamentos estiverem ativos, eles aguardarão na fila até que um encadeamento esteja disponível. Se algum encadeamento terminar devido a uma falha durante a execução anterior ao encerramento, um novo substituirá, se necessário, para executar tarefas subseqüentes. Os encadeamentos no pool existirão até que seja explicitamente encerrado.
Cria um conjunto de encadeamentos que cria novos encadeamentos, conforme necessário, mas reutiliza encadeamentos construídos anteriormente quando estiverem disponíveis. Esses pools geralmente melhoram o desempenho dos programas que executam muitas tarefas assíncronas de curta duração. As chamadas a serem executadas reutilizarão os threads construídos anteriormente, se disponíveis. Se nenhum encadeamento existente estiver disponível, um novo encadeamento será criado e adicionado ao pool. Os segmentos que não foram usados por sessenta segundos são encerrados e removidos do cache. Portanto, um pool que permanecer ocioso por tempo suficiente não consumirá nenhum recurso. Observe que conjuntos com propriedades semelhantes, mas detalhes diferentes (por exemplo, parâmetros de tempo limite) podem ser criados usando os construtores ThreadPoolExecutor.
Em termos de recursos, o newFixedThreadPool
manterá todos os threads em execução até que sejam explicitamente finalizados. Nos newCachedThreadPool
Threads que não foram usados por sessenta segundos, são encerrados e removidos do cache.
Diante disso, o consumo de recursos dependerá muito da situação. Por exemplo, se você tiver um grande número de tarefas de execução longa, sugiro o FixedThreadPool
. Quanto CachedThreadPool
aos documentos, os documentos dizem que "esses pools geralmente melhoram o desempenho de programas que executam muitas tarefas assíncronas de curta duração".
newCachedThreadPool
pode causar alguns problemas sérios , porque você deixa todo o controle parathread pool
e quando o serviço está trabalhando com outras pessoas no mesmo host , o que pode causar a falha de outras devido à espera de CPU por muito tempo. Então, acho quenewFixedThreadPool
pode ser mais seguro nesse tipo de cenário. Além disso, este post esclarece as diferenças mais marcantes entre eles.Apenas para completar as outras respostas, gostaria de citar Effective Java, 2nd Edition, de Joshua Bloch, capítulo 10, Item 68:
fonte
Se você olhar o código-fonte , verá que eles estão chamando ThreadPoolExecutor. internamente e definindo suas propriedades. Você pode criar o seu para ter um melhor controle de seus requisitos.
fonte
Se você não estiver preocupado com uma fila ilimitada de tarefas Chamadas / Executáveis , poderá usar uma delas. Como sugerido por bruno, eu também prefiro
newFixedThreadPool
quenewCachedThreadPool
ao longo destes dois.Mas ThreadPoolExecutor fornece recursos mais flexíveis em relação a um ou outro
newFixedThreadPool
ounewCachedThreadPool
Vantagens:
Você tem controle total do tamanho do BlockingQueue . Não é ilimitado, ao contrário das duas opções anteriores. Não receberei um erro de falta de memória devido a um grande acúmulo de tarefas pendentes de chamar / executar quando houver turbulência inesperada no sistema.
Você pode implementar uma política de tratamento de rejeição personalizada OU usar uma das políticas:
Por padrão
ThreadPoolExecutor.AbortPolicy
, o manipulador lança um RejectedExecutionException em tempo de execução após a rejeição.Em
ThreadPoolExecutor.CallerRunsPolicy
, o encadeamento que chama a execução executa a tarefa. Isso fornece um mecanismo simples de controle de feedback que diminuirá a taxa de envio de novas tarefas.Em
ThreadPoolExecutor.DiscardPolicy
, uma tarefa que não pode ser executada é simplesmente descartada.Em
ThreadPoolExecutor.DiscardOldestPolicy
, se o executor não for desligado, a tarefa na cabeça da fila de trabalho será descartada e a execução será tentada novamente (o que pode falhar novamente, fazendo com que isso se repita).Você pode implementar uma fábrica de threads personalizada para os casos de uso abaixo:
fonte
Isso mesmo,
Executors.newCachedThreadPool()
não é uma ótima opção para código de servidor que atende a vários clientes e solicitações simultâneas.Por quê? Existem basicamente dois problemas relacionados:
É ilimitado, o que significa que você está abrindo a porta para alguém prejudicar sua JVM simplesmente injetando mais trabalho no serviço (ataque de DoS). Os threads consomem uma quantidade não desprezível de memória e também aumentam o consumo de memória com base no trabalho em andamento; portanto, é muito fácil derrubar um servidor dessa maneira (a menos que você tenha outros disjuntores instalados).
O problema ilimitado é exacerbado pelo fato de o Executor ser liderado por um, o
SynchronousQueue
que significa que há uma transferência direta entre o responsável pela tarefa e o pool de threads. Cada nova tarefa criará um novo thread se todos os threads existentes estiverem ocupados. Geralmente, essa é uma estratégia ruim para o código do servidor. Quando a CPU fica saturada, as tarefas existentes levam mais tempo para serem concluídas. Ainda mais tarefas estão sendo enviadas e mais threads são criados, portanto, as tarefas demoram mais e mais para serem concluídas. Quando a CPU está saturada, mais threads definitivamente não é o que o servidor precisa.Aqui estão as minhas recomendações:
Use um conjunto de encadeamentos de tamanho fixo Executors.newFixedThreadPool ou ThreadPoolExecutor. com um número máximo definido de threads;
fonte
A
ThreadPoolExecutor
classe é a implementação básica para os executores retornados de muitos dosExecutors
métodos de fábrica. Então, vamos abordar os conjuntos de encadeamentos fixos e armazenados em cache daThreadPoolExecutor
perspectiva.ThreadPoolExecutor
O construtor principal desta classe é semelhante a este:
Tamanho do Pool Principal
O
corePoolSize
determina o tamanho mínimo do conjunto de encadeamentos de destino. A implementação manteria um pool desse tamanho, mesmo que não haja tarefas a serem executadas.Tamanho máximo da piscina
O
maximumPoolSize
é o número máximo de encadeamentos que podem estar ativos ao mesmo tempo.Depois que o conjunto de encadeamentos cresce e se torna maior que o
corePoolSize
limite, o executor pode encerrar encadeamentos ociosos e alcançar ocorePoolSize
novo. SeallowCoreThreadTimeOut
for verdade, o executor pode até finalizar os encadeamentos do pool principal se eles estiverem ociosos mais do que okeepAliveTime
limite.Portanto, o ponto principal é que, se os threads permanecerem ociosos mais do que o
keepAliveTime
limite, eles poderão ser encerrados, pois não há demanda por eles.Filas
O que acontece quando uma nova tarefa entra e todos os threads principais são ocupados? As novas tarefas serão enfileiradas dentro dessa
BlockingQueue<Runnable>
instância. Quando um encadeamento fica livre, uma dessas tarefas na fila pode ser processada.Existem implementações diferentes da
BlockingQueue
interface em Java, para que possamos implementar abordagens de enfileiramento diferentes, como:Fila limitada : Novas tarefas seriam colocadas em fila dentro de uma fila de tarefas limitada.
Fila não vinculada : Novas tarefas seriam colocadas em fila dentro de uma fila de tarefas ilimitada. Portanto, essa fila pode crescer tanto quanto o tamanho da pilha permitir.
Entrega Síncrona : Também podemos usar o
SynchronousQueue
para enfileirar as novas tarefas. Nesse caso, ao enfileirar uma nova tarefa, outro encadeamento já deve estar esperando por essa tarefa.Submissão do Trabalho
Aqui está como ele
ThreadPoolExecutor
executa uma nova tarefa:corePoolSize
threads estiverem em execução, tentará iniciar um novo thread com a tarefa especificada como seu primeiro trabalho.BlockingQueue#offer
método Ooffer
método não será bloqueado se a fila estiver cheia e retornar imediatamentefalse
.offer
retornarfalse
), ele tentará adicionar um novo encadeamento ao pool de encadeamentos com essa tarefa como seu primeiro trabalho.RejectedExecutionHandler
.A principal diferença entre os conjuntos de encadeamentos fixo e em cache se resume a esses três fatores:
Conjunto de segmentos fixos
Eis como
Excutors.newFixedThreadPool(n)
funciona:Como você pode ver:
OutOfMemoryError
.Um conjunto de encadeamentos de tamanho fixo parece ser um bom candidato quando vamos limitar o número de tarefas simultâneas para fins de gerenciamento de recursos .
Por exemplo, se vamos usar um executor para lidar com solicitações de servidor da Web, um executor fixo pode lidar com as explosões de solicitações de maneira mais razoável.
Para um gerenciamento de recursos ainda melhor, é altamente recomendável criar um costume
ThreadPoolExecutor
com umaBlockingQueue<T>
implementação limitada juntamente com razoávelRejectedExecutionHandler
.Conjunto de threads em cache
Eis como
Executors.newCachedThreadPool()
funciona:Como você pode ver:
Integer.MAX_VALUE
. Praticamente, o conjunto de encadeamentos é ilimitado.SynchronousQueue
sempre falha quando não há ninguém do outro lado para aceitá-la!Use-o quando você tiver muitas tarefas previsíveis de execução curta.
fonte
Você deve usar newCachedThreadPool apenas quando tiver tarefas assíncronas de curta duração, conforme declarado no Javadoc. Se você enviar tarefas que demoram mais tempo para serem processadas, você acabará criando muitos threads. Você pode atingir 100% da CPU se enviar tarefas de execução longa a uma taxa mais rápida para o newCachedThreadPool ( http://rashcoder.com/be-careful- while-using-executors-newcachedthreadpool/ ).
fonte
Faço alguns testes rápidos e tenho as seguintes descobertas:
1) se estiver usando SynchronousQueue:
Depois que os encadeamentos atingirem o tamanho máximo, qualquer novo trabalho será rejeitado, com a exceção abaixo.
2) se estiver usando o LinkedBlockingQueue:
Os encadeamentos nunca aumentam do tamanho mínimo para o tamanho máximo, o que significa que o conjunto de encadeamentos é tamanho fixo como o tamanho mínimo.
fonte