Parece ser impossível criar um pool de threads em cache com um limite para o número de threads que ele pode criar.
Aqui está como Executors.newCachedThreadPool estático é implementado na biblioteca Java padrão:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Portanto, usando esse modelo para criar um pool de encadeamentos em cache de tamanho fixo:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());
Agora, se você usar isso e enviar três tarefas, tudo ficará bem. O envio de outras tarefas resultará em exceções de execução rejeitadas.
Tentando isso:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());
Resultará na execução seqüencial de todos os threads. Ou seja, o pool de threads nunca criará mais de um thread para lidar com suas tarefas.
Este é um erro no método de execução do ThreadPoolExecutor? Ou talvez isso seja intencional? Ou existe alguma outra maneira?
Editar: eu quero algo exatamente como o pool de threads em cache (ele cria threads sob demanda e os mata após algum tempo limite), mas com um limite no número de threads que ele pode criar e a capacidade de continuar na fila de tarefas adicionais depois atingiu seu limite de threads. De acordo com a resposta de sjlee, isso é impossível. Olhando para o método execute () do ThreadPoolExecutor, é realmente impossível. Eu precisaria subclassificar ThreadPoolExecutor e substituir execute () da mesma forma que o SwingWorker, mas o que o SwingWorker faz em seu execute () é um hack completo.
fonte
Respostas:
O ThreadPoolExecutor possui os seguintes comportamentos principais, e seus problemas podem ser explicados por esses comportamentos.
Quando as tarefas são enviadas,
No primeiro exemplo, observe que o SynchronousQueue possui essencialmente tamanho 0. Portanto, no momento em que você atinge o tamanho máximo (3), a política de rejeição entra em ação (nº 4).
No segundo exemplo, a fila de escolha é um LinkedBlockingQueue que possui um tamanho ilimitado. Portanto, você fica preso ao comportamento # 2.
Você não pode realmente mexer muito com o tipo em cache ou o tipo fixo, pois o comportamento deles é quase completamente determinado.
Se você deseja ter um conjunto de encadeamentos dinâmico e limitado, é necessário usar um tamanho de núcleo positivo e um tamanho máximo combinados com uma fila de tamanho finito. Por exemplo,
Adendo : esta é uma resposta bastante antiga e parece que o JDK mudou seu comportamento quando se trata de um tamanho de núcleo igual a 0. Desde o JDK 1.6, se o tamanho do núcleo for 0 e o pool não tiver nenhum thread, o ThreadPoolExecutor adicionará um thread para executar essa tarefa. Portanto, o tamanho do núcleo de 0 é uma exceção à regra acima. Obrigado Steve por trazer isso à minha atenção.
fonte
allowCoreThreadTimeOut
para tornar essa resposta perfeita. Veja a resposta de @ user1046052A menos que eu tenha perdido algo, a solução para a pergunta original é simples. O código a seguir implementa o comportamento desejado, conforme descrito no pôster original. Gerará até 5 threads para trabalhar em uma fila ilimitada e os threads inativos serão encerrados após 60 segundos.
fonte
Teve o mesmo problema. Como nenhuma outra resposta reúne todas as questões, estou adicionando a minha:
Agora está claramente escrito nos documentos : Se você usar uma fila que não bloqueia (
LinkedBlockingQueue
) a configuração de threads máximos não terá efeito, apenas os threads principais serão usados.tão:
Este executor possui:
Nenhum conceito de threads máximos, pois estamos usando uma fila ilimitada. Isso é bom porque essa fila pode fazer com que o executor crie um grande número de threads extras que não são essenciais, se seguir sua política usual.
Uma fila de tamanho máximo
Integer.MAX_VALUE
.Submit()
será lançadoRejectedExecutionException
se o número de tarefas pendentes excederInteger.MAX_VALUE
. Não tenho certeza se ficaremos sem memória primeiro ou isso acontecerá.Possui 4 threads principais possíveis. Os encadeamentos ociosos do núcleo saem automaticamente se ociosos por 5 segundos. Então, sim, encadeamentos estritamente sob demanda. O número pode ser variado usando o
setThreads()
métodoGarante que o número mínimo de threads do núcleo nunca seja menor que um, ou então
submit()
rejeitará todas as tarefas. Como os threads principais precisam ser> = max threads, o método tambémsetThreads()
define o máximo de threads, embora a configuração do max thread seja inútil para uma fila ilimitada.fonte
No seu primeiro exemplo, as tarefas subseqüentes são rejeitadas porque
AbortPolicy
é o padrãoRejectedExecutionHandler
. O ThreadPoolExecutor contém as seguintes políticas, que você pode alterar através dosetRejectedExecutionHandler
método:Parece que você deseja um pool de threads em cache com um CallerRunsPolicy.
fonte
Nenhuma das respostas aqui resolveu meu problema, que tinha a ver com a criação de uma quantidade limitada de conexões HTTP usando o cliente HTTP do Apache (versão 3.x). Como levei algumas horas para descobrir uma boa configuração, vou compartilhar:
Isso cria um
ThreadPoolExecutor
que começa com cinco e mantém um máximo de dez threads em execução simultânea usandoCallerRunsPolicy
para execução.fonte
De acordo com o Javadoc para ThreadPoolExecutor:
(Ênfase minha.)
A resposta do jitter é o que você deseja, embora a minha responda à sua outra pergunta. :)
fonte
existe mais uma opção. Em vez de usar o novo SynchronousQueue, você também pode usar qualquer outra fila, mas é necessário garantir que o tamanho seja 1, para forçar o serviço do executor a criar um novo encadeamento.
fonte
Não parece que nenhuma das respostas realmente responda à pergunta - na verdade, não vejo uma maneira de fazer isso - mesmo se você subclasse de PooledExecutorService, pois muitos dos métodos / propriedades são privados, por exemplo, tornando protegido o addIfUnderMaximumPoolSize, você poderia faça o seguinte:
O mais próximo que cheguei foi disso - mas mesmo isso não é uma solução muito boa
ps não testou o acima
fonte
Aqui está outra solução. Eu acho que essa solução se comporta como você deseja (embora não tenha orgulho dessa solução):
fonte
É isso que você quer (pelo menos eu acho). Para uma explicação, verifique a resposta de Jonathan Feinberg
Executors.newFixedThreadPool(int n)
fonte
Você pode usar
ThreadPoolExecutor
como sugerido por @sjleeVocê pode controlar o tamanho do pool dinamicamente. Dê uma olhada nesta pergunta para obter mais detalhes:
Pool de segmentos dinâmicos
OU
Você pode usar a API newWorkStealingPool , que foi introduzida com o java 8.
Por padrão, o nível de paralelismo é definido como o número de núcleos da CPU em seu servidor. Se você tiver um servidor de CPU com 4 núcleos, o tamanho do conjunto de encadeamentos será 4. Essa API retorna o
ForkJoinPool
tipo deExecutorService
e permite o trabalho de roubar encadeamentos ociosos roubando tarefas de encadeamentos ocupados no ForkJoinPool.fonte
O problema foi resumido da seguinte maneira:
Antes de apontar para a solução, explicarei por que as seguintes soluções não funcionam:
Isso não enfileirará nenhuma tarefa quando o limite de 3 for atingido porque SynchronousQueue, por definição, não pode conter nenhum elemento.
Isso não criará mais de um thread único porque o ThreadPoolExecutor cria apenas threads que excedem o corePoolSize se a fila estiver cheia. Mas o LinkedBlockingQueue nunca está cheio.
Isso não reutilizará os threads até que o corePoolSize seja atingido porque ThreadPoolExecutor aumenta o número de threads até que o corePoolSize seja atingido, mesmo que os threads existentes estejam ociosos. Se você pode viver com essa desvantagem, esta é a solução mais fácil para o problema. É também a solução descrita em "Concorrência Java na Prática" (nota de rodapé na p172).
A única solução completa para o problema descrito parece ser a que envolve substituir o
offer
método da fila e escrever umRejectedExecutionHandler
como explicado nas respostas a esta pergunta: Como fazer com que o ThreadPoolExecutor aumente o número máximo de threads antes de enfileirar?fonte
Isso funciona para Java8 + (e outros, por enquanto ..)
onde 3 é o limite da contagem de threads e 5 é o tempo limite para threads inativos.
Se você deseja verificar se funciona , aqui está o código para fazer o trabalho:
saída do código acima para mim é
fonte