Threads ou ThreadPool? ThreadPool fixo ou dinâmico?

8

Eu tenho um programa java que escuta em uma porta para entrada. Com base na entrada, ele chama um serviço da web e retorna um sucesso / falha de volta ao programa cliente.

Eu bifurco um fio para cada conexão do cliente. A resposta de volta ao cliente que se conecta ao programa deve ser rápida.

Essas são as escolhas que estou considerando

  1. use threads regulares
  2. use ExecutorServicecomnewFixedThreadPool
  3. use ExecutorServicecomnewCachedThreadPool

A razão pela qual estou considerando Pools é porque meus threads têm vida curta - eles apenas chamam um serviço da web, retornam o resultado ao cliente e fecham a conexão.

Eu não acho newFixedThreadPoolque seria a coisa certa, porque então as conexões estariam esperando nas filas para obter um encadeamento.

newCachedThreadPoolteria sido perfeito, exceto por uma coisa - os fios morrem depois de um minuto. No meu caso, recebo rajadas de conexões - ou seja, várias conexões e, em seguida, pode haver uma pausa por alguns minutos e, novamente, rajadas. Eu acho que os threads no CachedThreadPool morrem e precisam ser recriados novamente - portanto, nesse caso, pode funcionar como o número 1 às vezes.

Idealmente, eu adoraria ter newCachedThreadPoolum mínimo - ou seja, uma configuração que diz que o número de threads nunca ficaria abaixo da palavra 20. Assim, os threads inativos são eliminados, mas nunca permitem que eles fiquem abaixo de um limite mínimo.

Existe algo assim disponível? Ou existem alternativas melhores?

user93353
fonte
Dê uma olhada no QuickServer.org em vez do próprio servidor de soquete. Ele faz o que você deseja -> soquete e threads e tem muitas amostras. pode funcionar em 30 minutos. Testado e estável, utilizou-o em 3 projetos. "Open source Java biblioteca / quadro para a criação rápida de aplicativos de servidor TCP multi-cliente robusto"
tgkprog

Respostas:

8

Os métodos na classe Executors são apenas métodos de conveniência para casos de uso comuns. Há muito mais opções disponíveis para a criação de conjuntos de encadeamentos.

Para criar a mesma coisa que Executors.newCachedThreadPool () faz, mas com um mínimo de 20 threads (isso é copiado do método em Executors, com o tamanho do thread principal alterado de 0 para 20).

        return new ThreadPoolExecutor(20, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
Michael Krussel
fonte
4

Pergunta interessante.

Eu recomendaria contra newCachedThreadPool. Irá gerar quantos threads forem necessários, sem nenhum limite superior. Isso é ruim!

A abordagem sugerida por Michael parece boa. Use ThreadPoolExecutore use um número com o qual você se sinta confortável como o tamanho do pool principal. Defina o número máximo de threads para algo que você deseja tolerar (ou seu servidor pode manipular).

Observe que não há praticamente nada que você possa fazer quando esgotar o recurso (a fila está cheia e o número máximo de threads está funcionando). Nesse caso, você pode fazer uma das duas coisas: descartar novas conexões ou aplicar contrapressão (não aceite novos trabalhos bloqueando). Por padrão, o TPE lança RejectedExecutionExceptionquando está cheio, mas é fácil implementar o comportamento de bloqueio. De fato, aqui está uma implementação de código aberto do BlockingThreadPoolExecutor .

goblinjuice
fonte
2

Em relação à RejectedExecutionException:

Em vez de um BlockingThreadPoolExecutor, podemos simplesmente definir o RejectedExecutionHandler no ThreadPoolExecutor para usar o manipulador CallerRunPolicy .

silmarx
fonte
0

Eu acho que usar um invólucro sobre um ThreadPoolExecutor é um bom caminho a percorrer. Um wrapper para que você possa expor alguma inicialização (como um nome de pool, número de threads, etc.) e um método para adicionar uma tarefa (Runnable) a um pool criado anteriormente com um dos métodos init.

O wrapper pode alterar o pool usado sem afetar outro código, além de expor outros métodos como número de threads, auditorias (tarefa antes / depois) de maneira consistente

Você também pode implementar sua própria fábrica de Threads para seu pool, que pode ter um nome personalizado, prioridade e, no mínimo, gancho em um UncaughtExceptionHandler para poder registrar erros.

no meu invólucro de piscina, temos métodos como

public static boolean queqeAdd(String poolName, int coreSize, int maxSize, boolean usePriorityQ, String threadNamePrefix, int timeOutSeconds) {...}
public static void execute(String poolName, Runnable command, Integer priority){...}
public static long tasksCount(String poolName) 
public static long tasksCompletedCount(String poolName) 
public static int clearPoolRequest(String poolName, boolean intrpt) 
public static int shutdownPoolRequest(String poolName) 
public static void executeAll(String poolName, List<Runnable> lst) 
tgkprog
fonte