[Java programação simultânea] pontos de conhecimento relacionados ao pool de threads finalização

Por que usar o pool de threads?

Tecnologia de pool: reduza o consumo de recursos a cada vez e melhora a utilização dos recursos.

O pool de threads fornece uma maneira de limitar e gerenciar recursos (incluindo a execução de uma tarefa). Cada conjunto de encadeamentos também mantém algumas estatísticas básicas, como o número de tarefas concluídas.

Benefícios do uso de pool de threads:

  • Reduza o consumo de recursos . Reduza o consumo causado pela criação e destruição de encadeamentos, reutilizando os encadeamentos criados.
  • Melhore a velocidade de resposta . Quando a tarefa chega, ela pode ser executada imediatamente, sem esperar até que o thread seja criado.
  • Melhore a capacidade de gerenciamento de threads . Threads são recursos escassos. Se forem criados ilimitadamente, não apenas consumirão recursos do sistema, mas também reduzirão a estabilidade do sistema.O pool de threads pode ser usado para alocação, ajuste e monitoramento uniformes.

Visualização recomendada: Portal

O princípio de realização do pool de threads?

executar o código fonte do método

    public void execute(Runnable command) {
        // 如果任务为null,则抛出异常。        if (command == null)
            throw new NullPointerException();        // ctl 中保存的线程池当前的一些状态信息  AtomicInteger        int c = ctl.get();        //判断当前线程池中执行的任务数量是否小于corePoolSize        if (workerCountOf(c) < corePoolSize) {
            //如果小于,则通过addWorker新建一个线程,然后,启动该线程从而执行任务。            if (addWorker(command, true))
                return;
            c = ctl.get();        }        //通过 isRunning 方法判断线程池状态        //线程池处于 RUNNING 状态才会被并且队列可以加入任务        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();            // 再次获取线程池状态,如果线程池状态不是 RUNNING 状态就需要从任务队列中移除任务。            // 并尝试判断线程是否全部执行完毕。同时执行拒绝策略。            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果当前线程池为空就新创建一个线程并执行。            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }        //通过addWorker新建一个线程,并将任务(command)添加到该线程中;
        //然后,启动该线程从而执行任务。        //如果addWorker执行失败,则通过reject()执行相应的拒绝策略的内容。        else if (!addWorker(command, false))
            reject(command);
    }

Quando o pool de threads cria um thread, ele encapsula o thread em um thread de trabalho. Depois que o trabalhador conclui a tarefa, ele obtém ciclicamente as tarefas da fila de trabalho para execução.

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {//循环执行任务
                w.lock();                //如果线程池正在停止,确保线程被中断
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&                      runStateAtLeast(ctl.get(), STOP))) &&                    !wt.isInterrupted())                    wt.interrupt();                try {
                    beforeExecute(wt, task);                    Throwable thrown = null;
                    try {
                        task.run();                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);                    }                } finally {
                    task = null;
                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);        }    }

 

Pontos de conhecimento relacionados ao pool de threads de "Programação simultânea Java"

 

  1. O pool de threads avalia se os threads no pool de threads principal [corePoolSize] estão realizando tarefas. Se não estiver, crie um novo thread de trabalho para executar a tarefa. Se todos os threads no pool de threads principais estão executando tarefas, entre no próximo processo.
  2. O pool de threads avalia se a fila de trabalho [BlockingQueue] está cheia. Se a fila de trabalho não estiver cheia, as tarefas enviadas recentemente são armazenadas nesta fila de trabalho. Se a fila de trabalho estiver cheia, entre no próximo processo.
  3. O thread pool avalia se os threads do pool de threads [maximumPoolSize] estão todos funcionando. Caso contrário, crie um novo thread de trabalho para executar a tarefa. Se estiver cheio, passe para a estratégia de saturação [RejectedExecutionHandler.rejectedExecution ()] para lidar com esta tarefa.

 

Pontos de conhecimento relacionados ao pool de threads de "Programação simultânea Java"

 

Como usar o pool de threads?

Análise importante de ThreadPoolExecutor

Parâmetros importantes do método de construção

Existem muitos parâmetros de construção do método ThreadPoolExecutor, podemos observar o mais longo:

    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;
    }
  • corePoolSize: O número de threads principais define o número mínimo de threads que podem ser executados simultaneamente .
  • maximumPoolSize: Quando as tarefas armazenadas na fila atingem a capacidade da fila , o número de threads que podem ser executados ao mesmo tempo torna-se o número máximo de threads . [Se você usar uma fila ilimitada, este parâmetro não terá efeito]
  • workQueue: Quando uma nova tarefa chega, ela primeiro determina se o número de threads em execução no momento atinge o número de threads principais.Se atingir o número de threads principais, a nova tarefa será armazenada na fila .
  • keepAliveTime: Quando o número de threads no pool de threads for maior que corePoolSize, se nenhuma tarefa for enviada neste momento, as threads fora da thread principal não serão destruídas imediatamente, mas esperarão até que o tempo de espera exceda keepAliveTime antes de serem recicladas e destruídas .
  • unit: a unidade de tempo de keepAliveTime.
  • threadFactory: usado para definir a fábrica para a criação de threads.Você pode definir um nome mais significativo para cada thread criado através da fábrica de threads.
  • handler: Estratégia de saturação. Quando o número de threads atualmente em execução ao mesmo tempo atinge o número máximo de threads [maximumPoolSize] e a fila está cheia, a estratégia de saturação é executada.

Uso simples de pool de threads

public class ThreadPoolTest {
    private static final int CORE_POOL_SIZE = 5; //核心线程数
    private static final int MAX_POOL_SIZE = 10; //最大线程数
    private static final int QUEUE_CAPACITY = 100; //任务队列的容量
    private static final Long KEEP_ALIVE_TIME = 1L; //等待时间
    public static void main(String[] args) {        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(                CORE_POOL_SIZE,                MAX_POOL_SIZE,                KEEP_ALIVE_TIME,                TimeUnit.SECONDS,                new ArrayBlockingQueue<>(QUEUE_CAPACITY),                new ThreadPoolExecutor.AbortPolicy());        for(int i = 0; i < 10 ; i ++){
            Runnable worker = new MyRunnable(""+ i); //创建任务
            threadPool.execute(worker); //通过execute提交
        }        threadPool.shutdown();        while(!threadPool.isTerminated()){
        }        System.out.println("Finished all threads");
    }}class MyRunnable implements Runnable {    private String command;    MyRunnable(String s) {
        this.command = s;
    }    @Override    public void run() {        System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
        processCommand();        System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
    }    private void processCommand() {        try {            Thread.sleep(5000);
        } catch (InterruptedException e) {            e.printStackTrace();        }    }    @Override    public String toString() {        return this.command;
    }}

Quais são as filas de tarefas?

  • ArrayBlockingQueue: Uma fila de bloqueio limitada com base na estrutura da matriz, que classifica os elementos de acordo com o princípio FIFO.
  • LinkedBlockingQueue: Fila de bloqueio com base na estrutura de lista vinculada, classificando elementos por FIFO, a taxa de transferência é geralmente maior do que ArrayBlockingQueue, Executors.newFixedThreadPool () usa esta fila.
  • SynchronousQueue: uma fila de bloqueio que não armazena elementos. Cada operação de inserção deve esperar até que outro thread chame a operação de remoção, caso contrário, a operação de inserção foi bloqueada e a taxa de transferência é geralmente maior do que LinkedBlockingQueue. Esta fila é usada por Executors.newCachedThreadPool () .
  • PriorityBlockingQueue: Uma fila de bloqueio infinito com prioridade.

Quais são as estratégias de saturação?

  • ThreadPoolExecutor.AbortPolicy : lance uma RejectedExecutionException para rejeitar o processamento da nova tarefa. [Estratégia de saturação padrão]
  • ThreadPoolExecutor.CallerRunsPolicy [Fornece uma fila escalável]: Chamada para executar sua própria thread para executar a tarefa, ou seja, executar a tarefa rejeitada diretamente na thread que chama o método execute.Se o programa de execução for fechado, a tarefa será descartada . Portanto, essa estratégia reduzirá a velocidade de envio de novas tarefas e afetará o desempenho geral do programa. Se seu aplicativo pode tolerar esse atraso e você requer que qualquer solicitação de tarefa seja executada, você pode escolher esta estratégia. public void rejeitadoExecução (Runnable r, ThreadPoolExecutor e) {if (! e.isShutdown ()) {r.run ();}}
  • ThreadPoolExecutor.DiscardPolicy:  Não processe novas tarefas, apenas descarte-as.
  • ThreadPoolExecutor.DiscardOldestPolicy:  Esta política descartará a solicitação de tarefa não processada mais antiga e executará a tarefa atual. public void rejeitadoExecução (Executável r, ThreadPoolExecutor e) {if (! e.isShutdown ()) {e.getQueue (). poll (); e.execute (r);}}

Claro, você também pode personalizar a estratégia de rejeição de acordo com suas necessidades e precisa implementar RejectedExecutionHandler.

Como criar um pool de threads?

1. Use vários métodos de construção de ThreadPoolExecutor.

2. Três tipos de ThreadPoolExecutor podem ser criados por meio de Executores, a classe de ferramenta da estrutura do Executor.

O pool de thread forçado no "Alibaba Java Development Manual" não permite o uso de Executores para criá-lo, mas usa o método ThreadPoolExecutor . Este método de processamento permite que os alunos escrevam mais claramente as regras operacionais do pool de thread e evita o risco de esgotamento de recursos

As desvantagens dos Executores que retornam objetos do pool de threads são as seguintes:  FixedThreadPool e SingleThreadExecutor : O comprimento da fila permitido para solicitações é Integer.MAX_VALUE, que pode acumular um grande número de solicitações, levando a OOM. CachedThreadPool e ScheduledThreadPool  : O número de threads que podem ser criados é Integer.MAX_VALUE, que pode criar um grande número de threads, levando a OOM.

Qual é a diferença entre execute method e submit method?

  • O método execute () é usado para enviar tarefas que não requerem um valor de retorno, portanto, é impossível determinar se a tarefa foi executada com sucesso pelo pool de threads;
threadPool.execute(new Runnable() {
    @Override
    public void run() {
    }}); //通过execute提交
  • O método submit () é usado para enviar tarefas que requerem valores de retorno. O pool de threads retornará um objeto do tipo Future. Este objeto Future pode ser usado para determinar se a tarefa foi executada com sucesso e o valor de retorno pode ser obtido por meio do método get () de Future. O método get () bloqueará o thread atual até que a tarefa seja concluída e use get O método (tempo limite longo, unidade TimeUnit) bloqueará o thread atual por um período de tempo e retornará imediatamente. Nesse momento, a tarefa pode não ser concluída.
Future<Object> future = threadPool.submit(hasReturnValueTask);
try{
    Object s = future.get();
}catch(InterruptedException e){
    //处理中断异常
}catch(ExecutionException e){
    //处理无法执行任务异常
}finally{
    threadPool.shutdown();
}

Se você acha que este artigo é útil para você, você pode curtir e segui-lo para apoiá-lo, ou pode seguir minha conta pública. Há mais artigos técnicos sobre produtos secos e informações relacionadas compartilhando sobre ele, todos aprendem e progridem juntos!

 

Acho que você gosta

Origin blog.csdn.net/weixin_50205273/article/details/108682056
Recomendado
Clasificación