Os tópicos geralmente estão envolvidos no trabalho. Por exemplo, algumas tarefas geralmente são entregues a threads para execução assíncrona. Ou o programa do servidor estabelece uma tarefa de processamento de threads separada para cada solicitação. Além dos threads, como a conexão do banco de dados que usamos. Essas operações de criação, destruição ou abertura e fechamento têm um grande impacto no desempenho do sistema. Portanto, o uso de "piscina" é destacado.
1. Por que usar o pool de threads
Na implementação descrita na Seção 3.6.1, um novo thread trabalhador é atribuído a cada cliente. Quando o tópico do trabalhador se comunica com o cliente, o tópico é destruído. Este método de implementação tem as seguintes deficiências:
• A sobrecarga da criação e destruição do servidor (incluindo recursos de tempo e sistema) é muito alta. Este item não precisa ser explicado, você pode verificar o "processo de criação de threads". Além do trabalho realizado pela própria máquina, também precisamos instanciar e iniciar, que exigem ocupação de recursos de pilha.
• Além da sobrecarga de criar e destruir threads, os threads ativos também consomem recursos do sistema. Isso deve ser um consumo de recursos de pilha. Também é considerado adivinhar o número de conexões de banco de dados.
• Se o número de encadeamentos for fixo e cada encadeamento tiver um longo ciclo de declaração, a comutação de encadeamentos também será relativamente fixada. Diferentes sistemas operacionais têm diferentes ciclos de comutação, geralmente em torno de 20ms. A mudança mencionada aqui é transferir os direitos de uso da CPU entre os threads sob o agendamento da JVM e o sistema operacional subjacente. Se os threads forem frequentemente criados e destruídos, os threads serão alterados com frequência, porque depois que um thread for destruído, o direito de usar deverá ser fornecido ao thread pronto, para que o thread possa ter a chance de executar. Nesse caso, a alternância entre os roscos não segue o ciclo de comutação fixa do sistema, e a sobrecarga das roscas de comutação é ainda maior que a sobrecarga de criação e destruição.
Relativamente falando, usando pools de threads, alguns threads são pré-criados, que removem constantemente as tarefas da fila de trabalho e executam a tarefa. Quando o tópico do trabalhador conclui uma tarefa, ele continua a executar outra tarefa na fila de trabalho. As vantagens são as seguintes:
• Reduz o número de criação e destruição, e cada thread do trabalhador pode ser reutilizado o tempo todo e pode executar várias tarefas.
• O número de encadeamentos no pool de threads pode ser facilmente ajustado de acordo com a capacidade de carga do sistema para evitar falhas do sistema devido a recursos excessivos do sistema.
2. Implementação simples do pool de threads
Abaixo está um simples pool de threads escrito por mim, que também foi desenhado diretamente do livro de programação de rede Java
Tópico do pacote; importar java.util.LinkedList;/*** Implementação do pool de threads, com base no comprimento, comprimento máximo e comprimento da fila do pool de threads regular, podemos aumentar o número de implementações* @author han*/public classe mythreadpool estende ThreadGroup {//m Número de CPU --- RunTime.getRuntime (). // se deve fechar o booleano privado isclosed = false; // fila Private LinkedList <dunnable> WorkCoUe; // thread pool id private static int threadpoolid; private int threadid; public mythreadpool (int poolsize) {super ("mythreadpool."+shreadpoolid); ThreadpoolId ++; setDaemon (true); WorkQueue = new LinkedList <Cunnable> (); for (int i = 0; i <poolsize; i ++) {new workthread (). start (); }} // Aqui você pode alterar para o ConcurrentLinkedQueue para evitar a eficiência do uso do vazio público sincronizado sincronizado Execute (Runnable Task) {if (isClosed) {lança a nova ilegalstateException ("o pool de conexões foi fechado ..."); } else {workqueue.add (tarefa); notificar (); }} Runnable Syncronized protegido Gettask () lança interruptedException {while (workQueue.size () == 0) {if (isClosed) {return null; } espere(); } return workQueue.removefirst (); } public sincronizado void close () {if (! isClosed) {isClosed = true; workQueue.clear (); interromper(); }} public void junção () {sincronizado (this) {isClosed = true; notifyAll (); } Thread [] threads = new Thread [ActiveCount ()]; int conting = enumerar (threads); for (int i = 0; i <contagem; i ++) {try {threads [i] .Join (); } catch (Exceção e) {}}} classe WorkThread Extende Thread {public WorkThread () {super (mythreadpool.This, "WorkThread"+(ThreadID ++); System.out.println ("Criar ..."); } @Override public void run () {while (! IsInterrupted ()) {System.out.println ("run .."); Tarefa executável = nulo; tente {// Esta é uma tarefa de método de bloqueio = gettask (); } catch (Exceção e) {} if (tarefa! = null) {task.run (); } else {break; }}}}}}Este pool de threads define principalmente uma fila de trabalho e alguns threads pré-criados. Desde que o método executivo seja chamado, você poderá enviar tarefas ao thread.
Quando não houver tarefa, o encadeamento subsequente será bloqueado no getTask () até que uma nova tarefa chegue e seja despertada.
Juntar e fechar podem ser usados para fechar o pool de threads. A diferença é que a junção concluirá as tarefas na fila, enquanto fechará imediatamente a fila e interromperá todos os threads de trabalhadores. A interrupção () em Close () é equivalente a chamar a respectiva interrupção () contendo threads infantis no ThreadGroup. Portanto, quando um fio estiver esperando ou dormindo, uma interrupção será lançada.
A aula de teste é a seguinte:
classe pública testMythreadpool {public static void main (string [] args) lança interruptedException {mythreadpool pool = new mythreadpool (3); para (int i = 0; i <10; i ++) {pool.execute (new runnable () {@Override public void run () {try {thread.sleep (1000);} catch (interruptedException e) {} System.out.println ("Working ...");}); } pool.join (); //pool.close (); }}3. O pool de threads fornecido pela JDK Class Library
O Java fornece uma boa implementação do pool de threads, que é mais robusta e eficiente que a nossa própria implementação, e também possui funções mais poderosas.
O diagrama de classe é o seguinte:
Os idosos já deram uma boa explicação sobre esse tipo de pool de threads. Qualquer pool de threads Java sob o Baidu tem exemplos e tutoriais muito detalhados escritos, então não os repetirei aqui.
4. Pool de linhas de injeção de primavera
Ao usar a estrutura da mola, se usarmos os métodos fornecidos pela Java para criar um pool de threads, é muito inconveniente gerenciar em aplicativos multithread e não está em conformidade com a nossa idéia de usar o Spring. (Embora a primavera possa ser injetada por métodos estáticos)
De fato, a própria primavera também fornece uma boa implementação de pools de threads. Esta classe é chamada Threadpooltaskexecutor.
A configuração na primavera é a seguinte:
<bean id = "executorService"> <propriedade name = "corePoolSize" value = "$ {threadpool.corepoolsize}" /> <!-número mínimo de threads mantidos por thread pool-> <names de thread = "KeeteniveSeconds" Value = "$ {ThreadPool.KeePaliveSeCSs}" name = "maxpoolsize" value = "$ {threadpool.maxpoolsize}" /> <!-Número máximo de threads mantidos pelo pool de threads-> <propriedades name = "queuecapacity" value = "$ {shaderpool.queuecapacity} /> <!-o buffer de buffer usado por thread pool->5. Notas sobre o uso de piscinas de threads
•Impasse
Qualquer programa multithreaded tem o risco de deadlocking. O caso mais simples é que dois threads AB, A Holds Lock 1, Pedido Lock 2, B mantém o bloqueio 2 e a solicitação de Lock 1. (Essa situação também aparecerá no MySQL Exclusive Lock, e o banco de dados reportará diretamente uma mensagem de erro). Há outro impasse no pool de threads: supondo que todos os threads de trabalhadores no pool de threads estejam bloqueados ao executar suas respectivas tarefas, e elas estão aguardando o resultado da execução de uma determinada tarefa A. A tarefa A está na fila e não pode ser executada porque não há threads inativos. Dessa forma, todos os recursos do pool de threads serão bloqueados e os impasses serão gerados.
• Recursos insuficientes do sistema
Se o número de threads no pool de threads for muito grande, esses threads consumirão uma grande quantidade de recursos, incluindo memória e outros recursos do sistema, o que afeta seriamente o desempenho do sistema.
• Erro simultâneo
A fila de trabalho do pool de threads depende dos métodos Wait () e Notify () para permitir que o trabalhador obtenha tarefas em tempo hábil, mas esses dois métodos são difíceis de usar. Se o código estiver errado, as notificações poderão ser perdidas, fazendo com que o fio do trabalhador permaneça ocioso, ignorando as tarefas que precisam ser processadas na fila de trabalho. Porque é melhor usar alguns pools de threads mais maduros.
• vazamento de encadeamento
Um risco sério de usar pools de threads é o vazamento de threads. Para pools de threads com número fixo de threads de trabalhador, se o fio do trabalhador lançar uma hora de execução ou erro ao executar uma tarefa, e essas exceções ou erros não forem capturados, o encadeamento do trabalhador termina de forma anormal, fazendo com que o pool de threads perca permanentemente um thread. (Isso é tão interessante)
Outra situação é que o tópico do trabalhador está bloqueado ao executar uma tarefa. Se estiver aguardando os dados de entrada do usuário, mas o usuário não inserir dados, resultando no bloqueio do encadeamento o tempo todo. Esse tópico de trabalhador está apenas no nome e, na verdade, não executa nenhuma tarefa. Se todos os threads no pool de threads estiverem nesse estado, o pool de threads não poderá adicionar novas tarefas.
• Sobrecarga de tarefas
Quando há um grande número de tarefas na fila para serem executadas na fila do thread do trabalhador, essas próprias tarefas podem consumir muitos recursos do sistema e causar escassez de recursos.
Para resumir, ao usar pools de threads, os seguintes princípios devem ser seguidos:
1. Se a Tarefa A precisar esperar pelo resultado da execução da Tarefa B de forma síncrona durante a execução, a tarefa A não é adequada para ser adicionada à fila de trabalho do pool de threads. Se você precisar esperar que outras tarefas executem resultados como a Tarefa A ser adicionada à fila, pode causar um impasse
2. Se uma tarefa puder ser bloqueada e for bloqueada por um longo tempo, o tempo de tempo limite deve ser definido para evitar o bloqueio permanente do fio do trabalhador e causar vazamento de encadeamento. No programa do servidor, quando um thread está aguardando o cliente se conectar ou aguardar os dados enviados pelo cliente, ele pode causar bloqueio. Você pode definir o tempo das seguintes maneiras:
Ligue para o método SetSoTimeout do ServerSocket para definir o tempo de tempo limite para aguardar a conexão do cliente.
Para cada soquete conectado ao cliente, ligue para o método SetSoTimeout do soquete para definir o tempo de tempo limite aguardando o cliente enviar dados.
3. Entenda as características da tarefa e analise se a tarefa executa operações que geralmente bloqueiam operações de IO ou executa operações que não bloquearão. O primeiro ocupa a CPU intermitentemente, enquanto este tem uma taxa de utilização mais alta. Quanto tempo leva para concluir uma tarefa? É uma tarefa de curto prazo ou uma tarefa de longo prazo? Em seguida, classifique as tarefas de acordo com as características da tarefa e adicione diferentes tipos de tarefas à fila de trabalho de diferentes pools de threads. Dessa forma, cada pool de threads pode ser alocado e ajustado de acordo com as características da tarefa.
4. Redimensione o pool de threads. O tamanho ideal de um pool de threads depende principalmente do número de CPUs disponíveis no sistema e das características das tarefas na fila de trabalho. Se houver apenas uma fila de trabalho em um sistema com N CPUs, e todas elas são tarefas com natureza aritmética (não bloqueando), então quando o pool de roscas possui fios de trabalhador N ou N+1, o uso máximo da CPU geralmente será obtido.
Se a fila de trabalho contiver tarefas que executam operações de IO e geralmente bloqueiam, faça com que o tamanho do pool de threads excedesse o número de CPUs disponíveis, porque nem todos os threads de trabalho estão funcionando o tempo todo. Selecione uma tarefa típica e, em seguida, estimar a proporção do tempo de espera para o tempo que realmente ocupa a CPU para executar operações no projeto que executa essa tarefa. Para um sistema com N CPUs, aproximadamente os threads n*(1+wt/st) precisam ser definidos para garantir que a CPU esteja totalmente utilizada.
Obviamente, a utilização da CPU não é a única coisa a considerar no processo de ajustar os pools de threads. À medida que o número de trabalhos do pool de threads aumenta, a memória ou outras restrições de recursos também ocorrerão, como soquetes, alças de arquivo aberto ou conexões de banco de dados, etc. É necessário garantir que os recursos do sistema consumidos por multithreads estejam dentro do escopo da resistência do sistema.
5. Evite sobrecarga de tarefas. O servidor deve limitar o número de conexões simultâneas dos clientes com base na capacidade de carga do sistema. Quando a conexão do cliente excede o valor limite, o servidor pode rejeitar a conexão e fazer avisos amigáveis ou limitar o comprimento da fila.
Os vários métodos e respostas de implementação acima para os pools de threads Java são todo o conteúdo que compartilhei com você. Espero que você possa lhe dar uma referência e espero que você possa apoiar mais o wulin.com.