Já faz algum tempo que estou frustrado com o comportamento padrão ThreadPoolExecutor
que sustenta os ExecutorService
pools de threads que tantos de nós usamos. Para citar os Javadocs:
Se houver mais de corePoolSize, mas menos de threads maximumPoolSize em execução, um novo thread será criado apenas se a fila estiver cheia .
O que isso significa é que se você definir um pool de threads com o código a seguir, ele nunca iniciará o segundo thread porque o LinkedBlockingQueue
é ilimitado.
ExecutorService threadPool =
new ThreadPoolExecutor(1 /*core*/, 50 /*max*/, 60 /*timeout*/,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(/* unlimited queue */));
Somente se você tiver uma fila limitada e a fila estiver cheia, quaisquer threads acima do número do núcleo serão iniciados. Suspeito que um grande número de programadores Java multithreaded júnior não estão cientes desse comportamento do ThreadPoolExecutor
.
Agora eu tenho um caso de uso específico em que isso não é o ideal. Estou procurando maneiras, sem escrever minha própria classe TPE, de contornar isso.
Meus requisitos são para um serviço da web que está retornando a chamada para um terceiro possivelmente não confiável.
- Não quero fazer o retorno de chamada de forma síncrona com a solicitação da web, então quero usar um pool de threads.
- Normalmente recebo alguns desses por minuto, então não quero ter um
newFixedThreadPool(...)
com um grande número de threads que, em sua maioria, estão inativos. - De vez em quando, recebo uma explosão desse tráfego e desejo aumentar o número de threads para algum valor máximo (digamos 50).
- Preciso fazer o melhor possível para fazer todos os retornos de chamada, então quero enfileirar qualquer um adicional acima de 50. Não quero sobrecarregar o resto do meu servidor da web usando um
newCachedThreadPool()
.
Como posso contornar essa limitação de ThreadPoolExecutor
onde a fila precisa ser delimitada e cheia antes que mais threads sejam iniciados? Como posso fazer com que ele inicie mais threads antes de colocar as tarefas na fila?
Editar:
@Flavio faz uma boa observação sobre o uso de ThreadPoolExecutor.allowCoreThreadTimeOut(true)
para que os threads principais expirem e saiam. Eu considerei isso, mas ainda queria o recurso core-threads. Eu não queria que o número de threads no pool caísse abaixo do tamanho do núcleo, se possível.