Estratégia de rejeição de conjunto de threads e demonstração de código

Pool de threads:

Para evitar a criação de threads repetidamente, a aparência dos conjuntos de threads permite que os threads sejam reutilizados. Quando uma tarefa é enviada, um encadeamento é retirado do conjunto de encadeamentos.Quando a tarefa é concluída, o encadeamento não é fechado diretamente, mas o encadeamento é retornado ao conjunto de encadeamentos para uso por outras tarefas. Dessa maneira, a sobrecarga desnecessária de desempenho causada pela criação frequente de encadeamentos pode ser evitada.

Usar pool de threads:

Após a versão java1.5, recomenda-se o uso de conjuntos de encadeamentos para usar os três métodos estáticos que foram empacotados no pacote java.util.concurrent:

  1. Crie um conjunto de encadeamentos com um tamanho de encadeamento especificado
	Executor executor = Executors.newFixedThreadPool (int nThreads);
  1. Criar um pool de threads com 1 thread
 	Executor executor = Executors.newSingleThreadExecutor();
  1. Não especifique o tamanho, crie um pool de threads que possa adicionar segmentos dinamicamente
 	Executor executor = Executors.newCachedThreadPool();

O princípio do pool de threads:

De fato, os três métodos acima são, na verdade, os métodos de construção da classe ThreadPoolExecutor chamados, mas os parâmetros são inconsistentes. Vamos examinar os métodos de construção da classe ThreadPoolExecutor:

   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Use uma tabela para explicar o significado dos parâmetros requeridos por este método:

Nome do parâmetro Explique
int corePoolSize Tamanho do pool principal
int maximumPoolSize O tamanho do maior conjunto de encadeamentos
long keepAliveTime O tempo máximo de sobrevivência de encadeamentos inativos no conjunto de encadeamentos que excede corePoolSize
Unidade TimeUnit A unidade de tempo de keepAliveTime é uma variável enumerada
BlockingQueue < Executável > Bloqueando fila de tarefas
ThreadFactory threadFactory Fábrica de linhas
Manipulador RejectedExecutionHandler Estratégia de rejeição do conjunto de encadeamentos

Fluxo de trabalho do conjunto de encadeamentos

  1. Se o número de encadeamentos no conjunto de encadeamentos atual for menor que corePoolSize, toda vez que uma tarefa for enviada, um encadeamento será criado para executar a tarefa;
  2. Se o número de conjuntos de encadeamentos for maior que corePoolSize, sempre que um encadeamento for enviado, ele tentará enviá-lo para a fila de cache de tarefas.Se a adição for bem-sucedida, a tarefa executará a tarefa na fila de cache quando o encadeamento estiver inativo; se a adição falhar (geralmente Crie uma fila de cache limitada) e, em seguida, outro encadeamento será criado para executar esta tarefa (nesse caso, a soma do número máximo de encadeamentos MaximumPoolSize e o comprimento da fila de cache é menor que o número total de encadeamentos que você precisa criar).
  3. Se a soma do número máximo de threads MaximumPoolSize e o comprimento da fila de cache for maior que o número total de threads que você precisa criar, uma estratégia de rejeição de tarefa será adotada para processamento;
  4. Se o número de encadeamentos no conjunto de encadeamentos for maior que corePoolSize, se o tempo ocioso de um encadeamento exceder keepAliveTime, o encadeamento será encerrado até que o número de encadeamentos no conjunto de encadeamentos não seja maior que corePoolSize; se for permitido definir o tempo de sobrevivência para os encadeamentos no conjunto principal, o conjunto principal O tempo ocioso do encadeamento em mais de keepAliveTime, o encadeamento também será encerrado;
Código para demonstrar esse fluxo de trabalho
package RejectThread;

//需要执行的任务线程
public class SendNoticeTask implements Runnable {
    private int count;

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println (Thread.currentThread ().getName () + "号工人    开始进行"  + count + "号工作任务");
        try {
            Thread.currentThread ().sleep (5000);
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
        System.out.println (Thread.currentThread ().getName ()+"号工人   执行完成");
    }
}
package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        //工作流程情形一
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> ());
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.AbortPolicy ());//抛异常的拒绝策略
        for (int i = 0; i < 3; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}

Aqui, definimos o tamanho do pool de núcleos como 3, o número máximo de threads é 10 e a fila do cache de tarefas não é definida como um tamanho.Quando o número de threads que você enviar for menor ou igual a 3, o resultado será basicamente o mesmo:
Insira a descrição da imagem aquise redefinirmos o envio O número de tarefas, definimos o número de threads a serem executados como 6, a fila de cache não define o número:

package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        //工作流程情形二
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> ());
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.AbortPolicy ());//抛异常的拒绝策略
        for (int i = 0; i < 6; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}


Insira a descrição da imagem aqui
Veja os resultados: É possível observar que as três primeiras tarefas 0, 1 e 2 são executadas diretamente, e as três tarefas 3, 4, 5 são colocadas na fila de cache e aguarde até que as três primeiras threads estejam livres. Ele é retirado apenas da fila de cache para execução. No momento, não importa quantos encadeamentos enviados, a estratégia de rejeição não está implementada, pois pode ser enviada para a fila de cache.
Vamos analisar o caso de adicionar falha, definimos o tamanho da fila LinkedBlockingQueue como 1 Ou seja, apenas um segmento pode ser inserido e observe o resultado da execução:

package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        //工作流程情形二
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> (1));//队列数量为1
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.AbortPolicy ());//抛异常的拒绝策略
        for (int i = 0; i < 6; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}


Insira a descrição da imagem aquiVeja os resultados da execução: pode-se ver que as tarefas de trabalho 0, 1, 2, 4 e 5 criam threads diretamente e as executam, enquanto a tarefa de trabalho 3 é armazenada na fila de cache e aguarda a disponibilidade do thread. ele é executado.
na verdade, este processo é, antes de 0,1,2 três tarefas são criadas diretamente segmento de execução, porque o tamanho do corePoolSize é 3, e em seguida, veio a tarefa de 3,4,5, de jeito nenhum, para o cache Ele é adicionado à fila, mas o tamanho da fila de cache é 1, para que apenas um possa ser adicionado, ou seja, a tarefa 3, as tarefas 4 e 5 criam diretamente um novo encadeamento para execução e aguarde o encadeamento ficar ocioso quando estiver ocioso. Retire a tarefa 3 da fila para continuar a execução.

O caso três é explicado na estratégia de rejeição.

Estratégia de rejeição do conjunto de encadeamentos

Este artigo demonstra principalmente a estratégia de rejeição do conjunto de encadeamentos, existem 4 tipos no total:

Manipulador da estratégia de rejeição Explique
AbortPolicy Descartar a tarefa e lançar RejectedExecutionException
DescartarPolítica Descartar a tarefa, mas não lançar uma exceção
DiscardOldestPolicy Descarte a primeira tarefa na fila e tente executar a tarefa novamente (repita esse processo)
CallerRunsPolicy A tarefa é tratada pelo encadeamento de chamada (o mais comum é o encadeamento principal)

Nos negócios acima, quando a estratégia de rejeição é usada, configuramos o corePoolSize como 3, o maximumPoolSize é 10 e o tamanho da fila de cache é 1. Desta vez, realizamos 15 tarefas, desta vez usaremos a estratégia de rejeição, o código é o seguinte :

  • AbortPolicy: lançar política de rejeição de exceção:
 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        //工作流程情形二
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> (1));
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.AbortPolicy ());//抛异常的拒绝策略
        for (int i = 0; i < 15; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}

Os resultados são os seguintes: Observa-
Insira a descrição da imagem aquise que as tarefas 0, 1, 2 são executadas diretamente primeiro, a tarefa 3 entra na fila de cache e novos encadeamentos são criados para execução em 4, 5, 6, 7, 8, 9, 10 e o máximo foi atingido neste momento O número de criação do encadeamento é 10, ele não pode ser criado e a estratégia de rejeição é executada. As tarefas subsequentes 11, 12, 13, 14 são descartadas diretamente e uma exceção é lançada.

  • Descartar tarefas sem lançar políticas anormais DiscardPolicy
 public DiscardOldestPolicy() { }
package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        //工作流程情形二
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> (1));
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.DiscardPolicy ());//不抛异常的拒绝策略
        for (int i = 0; i < 15; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}

O resultado da execução é relativamente simples, sem análise:
Insira a descrição da imagem aqui

  • O encadeamento de chamada lida com a tarefa (o mais comum é o encadeamento principal) A estratégia CallerRunsPolicy
    alterou a saída do encadeamento que executava a tarefa para facilitar a visualização do efeito:
package RejectThread;

public class SendNoticeTask implements Runnable {
    private int count;

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println (Thread.currentThread ().getName () + "号工人    开始进行"  + count + "号工作任务");
        try {
            Thread.currentThread ().sleep (5000);
        } catch (InterruptedException e) {
            e.printStackTrace ();
        }
        System.out.println (Thread.currentThread ().getName ()+"号工人    执行完成"  + count + "号工作任务~~~~~~");
    }
}

Use a estratégia de CallerRunsPolicy:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

Observe aqui: aqui chame diretamente o run () que precisa executar o encadeamento, o que equivale a chamar diretamente o método (você pode entender a diferença entre chamar run () e start () para iniciar o encadeamento), porque ele é o principal Dentro do encadeamento, ele representa a estratégia que o executor executa. CallerRun significa isso em inglês.

package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> (1));
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.CallerRunsPolicy ());
        for (int i = 0; i < 15; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
    }
}

Veja o resultado da execução: o
Insira a descrição da imagem aquiresultado da execução é especial.Primeiro, ele não lança uma exceção.Segundo, o encadeamento principal (principal) é adicionado.A figura é o primeiro resultado pop-up.Os resultados da análise podem ser entendidos da seguinte forma: Esta estratégia é equivalente ao encadeamento principal. O conjunto de encadeamentos, ou seja, a capacidade máxima do encadeamento agora é 11, a tarefa 3 ingressou na fila de cache que definimos, ou seja, quando a tarefa 11 foi enviada, a estratégia de rejeição foi implementada e a tarefa foi atribuída ao encadeamento principal. O thread principal também leva tempo para executar a tarefa.Todos os loops para envio de tarefas são bloqueados aqui.Espere até que o thread principal conclua a execução antes de continuar a enviar a próxima tarefa.Neste momento, os threads no pool de threads já concluíram parcialmente a execução, para que possam continuar executando. Tarefas posteriores.

  • Descarte a tarefa no início da fila e tente executar a tarefa novamente (repita esse processo) DiscardOldestPolicy
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
package RejectThread;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        System.out.println("主线程结开始");
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<> (2));
        poolExecutor.setRejectedExecutionHandler (new ThreadPoolExecutor.DiscardOldestPolicy ());
        for (int i = 0; i < 13; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            poolExecutor.execute(task);
        }
        System.out.println("主线程结束");

Os resultados da execução são os seguintes: Os
Insira a descrição da imagem aquiresultados da análise podem ser vistos, o processo anterior é consistente, as tarefas 0, 1, 2 são criadas diretamente, 3, 4 são adicionadas à fila de cache, 5 a 11 threads recém-criados são executados e você envia 12 Tarefas, o fluxo de processamento do sistema é que você envie um novo encadeamento, exclua primeiro um encadeamento na fila de cache, chame o
método poolExecutor.execute () para executar novamente, até que todas as tarefas estejam atualizadas, para que a fila de cache seja descartada A primeira tarefa é a tarefa 3, para que as tarefas 4 e 12 sejam executadas posteriormente.

Sumário

A estratégia de rejeição do conjunto de encadeamentos é a dos quatro acima. Se o acima ainda não puder atender aos seus requisitos, você precisará personalizar a estratégia de rejeição. A prática é escrever uma classe para implementar a interface RejectedExecutionHandler e personalizar sua lógica de código:

package RejectThread;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class MyRejectPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (!executor.isShutdown()) {
            executor.getQueue().poll ();
            System.out.println("自定义的拒绝策略");
            executor.submit (r);
             //r.run ();
        }
    }
}

Realizar teste de código:

package RejectThread;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class RejectThreadTest {
    public static void main(String[] args) {
        System.out.println("主线程结开始");
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor (3, 10,
                0, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<> (2));
        poolExecutor.setRejectedExecutionHandler (new MyRejectPolicy());
        for (int i = 0; i < 14; i++) {
            SendNoticeTask task = new SendNoticeTask();
            task.setCount(i);
            executor.submit (r);  
        }
        System.out.println("主线程结束");

Resultados de saída: a
Insira a descrição da imagem aqui
análise anterior deve ser muito simples, principalmente ao enviar a tarefa nº 12, ela primeiro imprime nossa saída e, em seguida, usa o pool de threads para executar a tarefa. Nesse momento, é necessário aguardar.

Publicado 39 artigos originais · ganhou elogios 1 · vista 4620

Acho que você gosta

Origin blog.csdn.net/thetimelyrain/article/details/97523175
Recomendado
Clasificación