Uma introdução detalhada aos pools de threads



1. Tópico

1. O que é um tópico?

Thread é a menor unidade que o sistema operacional pode realizar agendamento de cálculo. Ele está incluído no processo e é a unidade operacional real do processo. Um thread refere-se a um único fluxo de controle sequencial em um processo. Vários threads podem ser executados simultaneamente em um processo, e cada thread executa diferentes tarefas em paralelo.

2. Como criar um tópico

2.1. Criando threads em JAVA

  
  
  
  
  
/** * 继承Thread类,重写run方法 */class MyThread extends Thread {    @Override    public void run() {        System.out.println("myThread..." + Thread.currentThread().getName());} }
/** * 实现Runnable接口,实现run方法 */class MyRunnable implements Runnable { @Override public void run() { System.out.println("MyRunnable..." + Thread.currentThread().getName());} }
/** * 实现Callable接口,指定返回类型,实现call方法 */class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "MyCallable..." + Thread.currentThread().getName();} }

2.2. Teste

  
  
  
  
  
public static void main(String[] args) throws Exception {    MyThread thread = new MyThread();    thread.run();   //myThread...main    thread.start(); //myThread...Thread-0        MyRunnable myRunnable = new MyRunnable();    Thread thread1 = new Thread(myRunnable);    myRunnable.run();   //MyRunnable...main    thread1.start();    //MyRunnable...Thread-1        MyCallable myCallable = new MyCallable();    FutureTask<String> futureTask = new FutureTask<>(myCallable);    Thread thread2 = new Thread(futureTask);    thread2.start();    System.out.println(myCallable.call());  //MyCallable...main    System.out.println(futureTask.get());   //MyCallable...Thread-2
}

2.3. Problema

Já que criamos um thread, por que os resultados são diferentes quando chamamos o método diretamente e quando chamamos o método start()? New Thread() realmente cria um thread?

2.4. Análise do problema

Quando chamamos o método diretamente, podemos ver que ele é o thread principal de execução, e chamar o método start() inicia um novo thread, isso significa que new Thread() não cria um thread, mas cria um thread no início ().
Então vamos dar uma olhada no método start() da classe Thread:
  
  
  
  
  
class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法         private Runnable target;
public synchronized void start() { ...
boolean started = false; try { start0(); //可以看到,start()方法真实的调用时start0()方法 started = true; } finally { ... } } private native void start0(); //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度
@Override public void run() { if (target != null) { target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务
} } }

2.5. Resumo

  • JAVA não pode criar threads diretamente para executar tarefas, em vez disso, ele chama o sistema operacional para iniciar o thread criando um objeto Thread, e o sistema operacional chama de volta o método run() da interface Runnable para executar a tarefa;
  • A forma de implementar Runnable separa as tarefas de retorno de chamada reais a serem executadas pelo thread, desacoplando assim a inicialização do thread das tarefas de retorno de chamada;
  • A maneira de implementar Callable é usar o modo Future não apenas para desacoplar a inicialização do thread da tarefa de retorno de chamada, mas também para obter o resultado da execução após a conclusão da execução;


2. Multiencadeamento

1. O que é multithreading?

Multithreading refere-se à tecnologia que permite a execução simultânea de vários threads de software ou hardware. O mesmo thread só pode processar uma tarefa antes de processar a próxima. Às vezes, precisamos processar várias tarefas ao mesmo tempo. Nesse caso, precisamos criar vários threads para processar as tarefas ao mesmo tempo.

2. Quais são os benefícios do multithreading?

2.1. Processamento serial

  
  
  
  
  
public static void main(String[] args) throws Exception {    System.out.println("start...");    long start = System.currentTimeMillis();    for (int i = 0; i < 5; i++) {        Thread.sleep(2000);  //每个任务执行2秒         System.out.println("task done..."); //处理执行结果    }    long end = System.currentTimeMillis();    System.out.println("end...,time = "  + (end - start));}//执行结果start...task done...task done...task done...task done...task done... end...,time = 10043

2.2. Processamento paralelo

  
  
  
  
  
public static void main(String[] args) throws Exception {    System.out.println("start...");    long start = System.currentTimeMillis();    List<Future> list = new ArrayList<>();
for (int i = 0; i < 5; i++) { Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); //每个任务执行2秒 return "task done..."; }
}; FutureTask task = new FutureTask(callable); list.add(task); new Thread(task).start();
} list.forEach(future -> { try { System.out.println(future.get()); //处理执行结果 } catch (Exception e) { } }); long end = System.currentTimeMillis(); System.out.println("end...,time = " + (end - start));
} //执行结果 start... task done... task done... task done... task done... task done... end...,time = 2005

2.3. Resumo

  • Multithreading pode dividir uma tarefa em várias subtarefas e várias subtarefas podem ser executadas simultaneamente. Cada subtarefa é um thread.
  • Multi-threading consiste em concluir várias tarefas simultaneamente, não para melhorar a eficiência operacional, mas para melhorar a eficiência do uso de recursos para melhorar a eficiência do sistema.

2.4. Problemas de multithreading

Podemos ver no exemplo acima que se criarmos um thread para cada tarefa, se houver muitas tarefas, criaremos um grande número de threads, o que pode levar ao esgotamento dos recursos do sistema. Ao mesmo tempo, sabemos que a execução de threads precisa aproveitar recursos da CPU. Se houver muitos threads, muito tempo será gasto na sobrecarga de troca de threads.
Além disso, cada tarefa requer a criação de um thread, e a criação de um thread requer a chamada do método subjacente do sistema operacional, o que é caro, e o thread é reciclado após a conclusão. Quando um grande número de threads é necessário, a criação de threads leva muito tempo.


3. Conjunto de threads

1. Como projetar um pool de threads

Como o desenvolvimento multithread tem alguns dos problemas acima, podemos projetar algo para evitá-los? Claro! O pool de threads nasceu para resolver esses problemas. Então, como devemos projetar um pool de threads para resolver esses problemas? Em outras palavras, quais funções um pool de threads deve ter?

1.1. Funções básicas do pool de threads

  • Multi-threading criará um grande número de threads para esgotar os recursos, portanto, o pool de threads deve limitar o número de threads para garantir que os recursos do sistema não se esgotem;
  • Cada vez que um novo thread é criado , a sobrecarga de criação aumentará, portanto, o pool de threads deve reduzir a criação de threads e tentar reutilizar threads já criados;

1.2. Pool de threads enfrenta problemas

  • Sabemos que os threads serão reciclados após a conclusão de suas tarefas, então como reutilizamos os threads?
  • Especificamos o número máximo de threads. Quando o número de tarefas excede o número de threads, como devemos lidar com isso?

1.3. A inovação vem da vida

Vamos primeiro supor um cenário: suponha que somos os gestores de uma empresa de logística. As mercadorias a serem entregues são as nossas tarefas e os camiões são as nossas ferramentas de entrega. É claro que não podemos preparar tantos camiões quantas as mercadorias. Então, quando os clientes continuam a nos entregar mercadorias, como devemos gerenciá-los para que a empresa possa funcionar da melhor forma?
  • Quando a mercadoria chegou ainda não tínhamos camiões, tínhamos que comprar um camião por cada lote de mercadoria a transportar;
  • Quando o transporte do caminhão for concluído e o próximo lote de mercadorias ainda não tiver chegado, o caminhão ficará estacionado no armazém e poderá ser transportado imediatamente quando a mercadoria chegar;
  • Quando tivermos um certo número de carros e acharmos que são suficientes, não compraremos mais carros.Se chegarem novas mercadorias neste momento, colocaremos as mercadorias primeiro no armazém e esperaremos que os carros voltem.
  • Quando chega a grande venda 618, há muitas mercadorias para serem entregues, os carros estão na estrada e os armazéns estão cheios. O que devemos fazer? Optamos por alugar temporariamente alguns carros para ajudar na entrega e melhorar a eficiência do entrega;
  • No entanto, ainda há muitas mercadorias. Adicionamos caminhões temporários, mas eles ainda não podem entregá-los. Neste momento, não temos escolha a não ser fazer os clientes esperarem na fila ou não aceitá-los.
  • Depois que a grande venda foi concluída com sucesso, os bens acumulados foram entregues.Para reduzir custos, devolvemos todos os carros alugados temporariamente;

1.4. A tecnologia vem da inovação

Com base no cenário acima, a empresa de logística é nosso pool de threads, as mercadorias são nossas tarefas de thread e os caminhões são nossos threads. A forma como projetamos o processo de gerenciamento de caminhões da empresa deve ser a mesma forma como projetamos o processo de gerenciamento do pool de threads.
  • Quando chega uma tarefa e ainda não temos uma thread, devemos criar uma thread para executar a tarefa;
  • Quando a tarefa do thread é concluída, o thread não é liberado e aguarda a chegada da próxima tarefa para então executá-la;
  • Quando o número de threads criados atinge um determinado valor, armazenamos novas tarefas e aguardamos a execução dos threads ociosos, o que exige que o pool de threads tenha um contêiner para armazenar tarefas;
  • Quando o contêiner estiver cheio, precisamos adicionar alguns threads temporários para melhorar a eficiência do processamento;
  • Quando uma tarefa não pode ser processada após adicionar um thread temporário, a tarefa deve ser rejeitada;
  • Quando todas as tarefas forem concluídas, os threads temporários deverão ser liberados para evitar sobrecarga desnecessária;

2. Análise detalhada do pool de threads

Acima falamos sobre como projetar um pool de threads, agora vamos ver como o mestre o projetou;

2.1 Como é projetado o pool de threads em JAVA?

2.1.1. Projeto do pool de threads

Dê uma olhada nas propriedades do pool de threads para entender o design do pool de threads.
  
  
  
  
  
public class ThreadPoolExecutor extends AbstractExecutorService {
//线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //值为29,用来表示偏移量 private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池的最大容量 private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池的运行状态,总共有5个状态,用高3位来表示 private static final int RUNNING = -1 << COUNT_BITS; //接受新任务并处理阻塞队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS; //不接受新任务但会处理阻塞队列中的任务
private static final int STOP = 1 << COUNT_BITS; //不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务
private static final int TIDYING = 2 << COUNT_BITS; //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法
private static final int TERMINATED = 3 << COUNT_BITS; // terminated()方法已经执行结束
//任务缓存队列,用来存放等待执行的任务 private final BlockingQueue<Runnable> workQueue;
//全局锁,对线程池状态等属性修改时需要使用这个锁 private final ReentrantLock mainLock = new ReentrantLock();
//线程池中工作线程的集合,访问和修改需要持有全局锁 private final HashSet<Worker> workers = new HashSet<Worker>();
// 终止条件 private final Condition termination = mainLock.newCondition();
//线程池中曾经出现过的最大线程数 private int largestPoolSize; //已完成任务的数量 private long completedTaskCount; //线程工厂 private volatile ThreadFactory threadFactory; //任务拒绝策略 private volatile RejectedExecutionHandler handler;
//线程存活时间 private volatile long keepAliveTime;
//是否允许核心线程超时 private volatile boolean allowCoreThreadTimeOut;
//核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0 private volatile int corePoolSize;
//最大池大小,不得超过CAPACITY private volatile int maximumPoolSize; //默认的任务拒绝策略 private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//运行权限相关 private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");
... }
Para resumir: A partir do design do pool de threads acima, pode-se ver que a função do pool de threads ainda está muito completa.
  • Fornece gerenciamento de criação de threads, número e tempo de sobrevivência;
  • Fornece gerenciamento de transferência de status do pool de threads;
  • São fornecidos vários contêineres para armazenamento em cache de tarefas;
  • Fornece um mecanismo para lidar com tarefas redundantes;
  • Fornece funções ;

2.1.2. Construtor de pool de threads

  
  
  
  
  
//构造函数 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.corePoolSize = corePoolSize;    this.maximumPoolSize = maximumPoolSize;    this.workQueue = workQueue;    this.keepAliveTime = unit.toNanos(keepAliveTime);    this.threadFactory = threadFactory;    this.handler = handler;}
Para resumir:
  • O construtor nos diz como usar o pool de threads e quais recursos do pool de threads podemos controlar;

2.1.3. Execução do pool de threads

2.1.3.1. Método de envio de tarefa
  • public void execute (comando executável);
  • Future<?> submit(Tarefa executável);
  • Envio futuro (tarefa executável, resultado T);
  • Envio futuro (tarefa chamável);
  
  
  
  
  
public Future<?> submit(Runnable task) {        if (task == null) throw new NullPointerException();        RunnableFuture<Void> ftask = newTaskFor(task, null);        execute(ftask);        return ftask;}
Você pode ver que o método subjacente do método submit também chama o método execute, portanto, analisamos apenas o método execute aqui;
  
  
  
  
  
    public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();                int c = ctl.get();        //第一步:创建核心线程        if (workerCountOf(c) < corePoolSize) {  //worker数量小于corePoolSize            if (addWorker(command, true))       //创建worker                return;            c = ctl.get();        }        //第二步:加入缓存队列        if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列            int recheck = ctl.get();                if (! isRunning(recheck) && remove(command))    //双重检查,若线程池状态关闭了,移除任务                reject(command);            else if (workerCountOf(recheck) == 0)       //线程池状态正常,但是没有线程了,创建worker                addWorker(null, false);        }        //第三步:创建临时线程        else if (!addWorker(command, false))            reject(command);    }
Para resumir: as principais funções do método execute():
  • Se o número de threads principais for insuficiente, crie threads principais;
  • Quando o thread principal estiver cheio, ele será adicionado à fila de cache;
  • Quando a fila de cache está cheia, threads não essenciais são adicionados;
  • Se os threads também estiverem cheios, a tarefa será rejeitada;
2.1.3.2. Criar tópicos
private boolean addWorker(Runnable firstTask, boolean core) {        retry:        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);
//等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()) //线程池已关闭,并且无需执行缓存队列中的任务,则不创建 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false;
for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) //CAS增加线程数 break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } }
//上面的流程走完,就可以真实开始创建线程了 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); //这里创建了线程 final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); //这里将线程加入到线程池中 int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); //添加成功,启动线程 workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); //添加线程失败操作 } return workerStarted; }
Resumo: As principais funções do método addWorker();
  • Aumentar o número de threads;
  • Crie uma instância de thread Worker e junte-se ao pool de threads;
  • Cadastre-se para completar o tópico de abertura;
  • Se a inicialização falhar, o processo adicional será revertido;
2.1.3.3. Implementação de threads de trabalho
    private final class Worker  //Worker类是ThreadPoolExecutor的内部类        extends AbstractQueuedSynchronizer          implements Runnable{                final Thread thread;    //持有实际线程        Runnable firstTask;     //worker所对应的第一个任务,可能为空        volatile long completedTasks;   //记录执行任务数
Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } public void run() { runWorker(this); //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用 }
...继承AQS,实现了不可重入锁... }

   
   
   
   
   
Resumo: As principais funções do thread de trabalho Classe Worker;
  • Esta classe contém um thread de trabalho e processa continuamente novas tarefas que obtém. O thread que contém é um thread reutilizável;
  • Esta classe pode ser considerada uma classe de adaptação.No método run(), o método runWorker() é na verdade chamado para obter continuamente novas tarefas e completar a reutilização do thread;
2.1.3.4. Reutilização de threads
final void runWorker(Worker w) {    //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用        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);    //执行任务前的Hook方法,可自定义                    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); //执行任务后的Hook方法,可自定义                    }                } finally {                    task = null;    //执行完成后,将当前线程中的任务制空,准备执行下一个任务                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;        } finally {            processWorkerExit(w, completedAbruptly);    //线程执行完成后的清理工作        }    }
Resumo: As principais funções do método runWorker();
  • 循环从缓存队列中获取新的任务,直到没有任务为止;
  • 使用worker持有的线程真实执行任务;
  • 任务都执行完成后的清理工作;
2.1.3.5、队列中获取待执行任务
  
  
  
  
  
private Runnable getTask() {        boolean timedOut = false;   //标识当前线程是否超时未能获取到task对象
for (;;) { int c = ctl.get(); int rs = runStateOf(c);
// Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; }
int wc = workerCountOf(c);
// Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) //若线程存活时间超时,则CAS减去线程数量 return null; continue; }
try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //允许超时回收则阻塞等待 workQueue.take(); //不允许则直接获取,没有就返回null if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
小结:getTask()方法主要功能;
实际在缓存队列中获取待执行的任务;
在这里管理线程是否要阻塞等待,控制线程的数量;
2.1.3.6、清理工作
private void processWorkerExit(Worker w, boolean completedAbruptly) {        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted            decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); //移除执行完成的线程 } finally { mainLock.unlock(); }
tryTerminate(); //每次回收完一个线程后都尝试终止线程池
int c = ctl.get(); if (runStateLessThan(c, STOP)) { //到这里说明线程池没有终止 if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); //异常终止线程的话,需要在常见一个线程 } }
小结:processWorkerExit()方法主要功能;
  • 真实完成线程池线程的回收;
  • 调用尝试终止线程池;
  • 保证线程池正常运行;
2.1.3.7、尝试终止线程池
final void tryTerminate() {        for (;;) {            int c = ctl.get();                        //若线程池正在执行、线程池已终止、线程池还需要执行缓存队列中的任务时,返回            if (isRunning(c) ||                runStateAtLeast(c, TIDYING) ||                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))                return;                            //执行到这里,线程池为SHUTDOWN且无待执行任务 或 STOP 状态            if (workerCountOf(c) != 0) {                interruptIdleWorkers(ONLY_ONE);     //只中断一个线程                return;            }
//执行到这里,线程池已经没有可用线程了,可以终止了 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //CAS设置线程池终止 try { terminated(); //执行钩子方法 } finally { ctl.set(ctlOf(TERMINATED, 0)); //这里将线程池设为终态 termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
小结:tryTerminate()方法主要功能;
  • 实际尝试终止线程池;
  • 终止成功则调用钩子方法,并且将线程池置为终态。

2.2、JAVA线程池总结

以上通过对JAVA线程池的具体分析我们可以看出,虽然流程看似复杂,但其实有很多内容都是状态重复校验、线程安全的保证等内容,其主要的功能与我们前面所提出的设计功能一致,只是额外增加了一些扩展,下面我们简单整理下线程池的功能;
2.2.1、主要功能
  • 线程数量及存活时间的管理;
  • 待处理任务的存储功能;
  • 线程复用机制功能;
  • 任务超量的拒绝功能;
2.2.2、扩展功能
  • 简单的执行结果统计功能;
  • 提供线程执行异常处理机制;
  • 执行前后处理流程自定义;
  • 提供线程创建方式的自定义;
2.2.3、流程总结
以上通过对JAVA线程池任务提交流程的分析我们可以看出,线程池执行的简单流程如下图所示;

2.3、JAVA线程池使用

线程池基本使用验证上述流程:
public static void main(String[] args) throws Exception {                //创建线程池       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(               5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));                //加入4个任务,小于核心线程,应该只有4个核心线程,队列为0        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 4        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0                //再加4个任务,超过核心线程,但是没有超过核心线程 + 缓存队列容量,应该5个核心线程,队列为3        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3                //再加4个任务,队列满了,应该5个热核心线程,队列5个,非核心线程2个        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 7        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5                //再加4个任务,核心线程满了,应该5个热核心线程,队列5个,非核心线程5个,最后一个拒绝        for (int i = 0; i < 4; i++) {            try {                threadPoolExecutor.submit(new MyRunnable());            } catch (Exception e) {                e.printStackTrace();    //java.util.concurrent.RejectedExecutionException            }        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 10        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5        System.out.println(threadPoolExecutor.getTaskCount());  //共执行15个任务                //执行完成,休眠15秒,非核心线程释放,应该5个核心线程,队列为0        Thread.sleep(1500);        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0                //关闭线程池        threadPoolExecutor.shutdown();    }
-end-

本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

雷军公布小米澎湃 OS 完整系统架构,称底层全部重构 语雀 10 月 23 日故障原因及修复过程公告 微软 CEO 纳德拉:放弃 Windows Phone 和移动业务是错误决策 Java 11 和 Java 17 使用率均超 Java 8 Hugging Face 被限制访问 语雀网络故障持续大约 10 小时,现已恢复正常 国家数据局正式揭牌 Oracle 推出 Visual Studio Code 的 Java 开发扩展 马斯克:如果维基百科改名“维鸡百科”就捐款 10 亿美元 MySQL 8.2.0 GA
{{o.name}}
{{m.name}}

Acho que você gosta

Origin my.oschina.net/u/4090830/blog/10136833
Recomendado
Clasificación