记JAVA线程池的一次扫盲

前言

某日同一同事探讨线程池的几个关键参数:corePoolSize,maximumPoolSize,queueCapacity;进而围绕这几个参数引出几个点:

  • a) 应用初始状态下,此时提交任务,将创建线程来处理任务;
  • b) 当运行的线程数达到corePoolSize时,新提交的任务将如何操作?
  • c) 当queueCapacity满时,新提交的任务将如何处理?
  • d) 当运行的线程数达到maximumPoolSize时,新提交的任务该如何处理?
  • e) corePoolSize+1个线程运行中,此时有一个工作任务处理完成,该完成任务的线程后续的状态是什么样的?

第一反应

以自己来设计线程池的思路出发,当时个人认为处理方式如下:

在场景b)时,应该会创建新线程来执行任务,直到线程数达到maximumPoolSize后,
再提交的任务将放在等待队列中,直到队列塞满(达到queueCapacity),
此时再提交的任务将根据配置的拒绝模式处理;

然而,被活生生的打脸,该同事好好喝了杯茶说:

JAVA中线程池的实现方式如下(默认maximumPoolSize > corePoolSize,queueCapacity有限大小):
1. 运行状态的线程数小于corePoolSize时,新增的任务将创建新线程来处理任务;
2. 当运行状态的线程数等于corePoolSize时,新增的任务将被放进任务队列中;
3. 当运行状态的线程数等于corePoolSize并且任务队列的大小达到queueCapacity时,新提交的任务将会创建新线程进行处理;
4. 当运行状态的线程数等于maximumPoolSize 并且任务队列大小达到queueCapacity,此时提交的任务将被拒绝;

故上述的b,c,d场景比较明确处理方式了;此时场景e该如何处理呢?
在细追代码之前,我们直接撸个代码看下运行结果:

  • 任务线程代码
package com.test.chan.pools;
import java.util.Date;
public class BlockThread implements Runnable {

    private String tKey = null;
    private long blockTS = 0L;
    public BlockThread(int blockTimes,String threadKey){
        this.blockTS = blockTimes * 1000L;
        this.tKey = threadKey;
    }

    public void run() {
        System.out.printf("[%s]======== START %s - %s ===============\n", new Date(),this.tKey, this.blockTS);
        try {
            Thread.sleep(this.blockTS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("[%s]======== END %s - %s ===============\n", new Date(),this.tKey, this.blockTS);
    }
}
  • 验证代码
package com.test.chan.pools;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo {
    public static void main(String[] args){
        int corePoolSize = 5;
        int maximumPoolSize = 15;
        long keepAliveTime = 0L;
        int queueCapacity = 10;
        ExecutorService exec = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                keepAliveTime, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue(queueCapacity));
        //前5个正常提交,任务阻塞时间未300s
        //前15个,有10个任务阻塞,任务阻塞时间10s
        //前16个,任务阻塞时间10s,观察队列中的任务是否被执行
        String key_pre = "STEP1-";
        int taskNum = 5;
        int blockTime = 300;
        //STEP1. 提交5个阻塞时间为300s的任务
        for(int i =0;i
  • 查看输出结果并分析
/** 初始提交任务  **/
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-0 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-4 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-3 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-2 - 300000 ===============
[Thu Aug 20 20:15:30 CST 2020]======== START STEP1-1 - 300000 ===============
/** 提交的任务进入任务队列中 **/
/** 任务队列满了,提交任务则将创建新线程处理该任务 **/
[Thu Aug 20 20:15:32 CST 2020]======== START STEP3-0 - 10000 ===============
[Thu Aug 20 20:15:42 CST 2020]======== END STEP3-0 - 10000 ===============
/** 完成任务的线程从任务队列中取任务执行 **/
[Thu Aug 20 20:15:42 CST 2020]======== START STEP2-0 - 10000 ===============
[Thu Aug 20 20:15:52 CST 2020]======== END STEP2-0 - 10000 ===============
[Thu Aug 20 20:15:52 CST 2020]======== START STEP2-1 - 10000 ===============
[Thu Aug 20 20:16:02 CST 2020]======== END STEP2-1 - 10000 ===============
[Thu Aug 20 20:16:02 CST 2020]======== START STEP2-2 - 10000 ===============
[Thu Aug 20 20:16:12 CST 2020]======== END STEP2-2 - 10000 ===============
[Thu Aug 20 20:16:12 CST 2020]======== START STEP2-3 - 10000 ===============
[Thu Aug 20 20:16:22 CST 2020]======== END STEP2-3 - 10000 ===============
[Thu Aug 20 20:16:22 CST 2020]======== START STEP2-4 - 10000 ===============
/**  任务队列满,新增线程处理新提交的任务 **/
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-5 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-13 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-7 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-6 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-8 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-12 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-11 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-10 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP4-9 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== END STEP2-4 - 10000 ===============
[Thu Aug 20 20:16:32 CST 2020]======== START STEP2-5 - 10000 ===============
/** 任务队列满了,运行的线程数达到maximumPoolSize,新提交任务被拒绝 **/
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.test.chan.pools.BlockThread@6e0be858 rejected from java.util.concurrent.ThreadPoolExecutor@61bbe9ba[Running, pool size = 15, active threads = 15, queued tasks = 10, completed tasks = 5]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    at com.test.chan.pools.ThreadPoolDemo.main(ThreadPoolDemo.java:67)
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-13 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-6 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-6 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-7 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-5 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-12 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-0 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-11 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-8 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-2 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-9 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP4-10 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-4 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-8 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-7 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-3 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP4-1 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== START STEP2-9 - 10000 ===============
[Thu Aug 20 20:16:42 CST 2020]======== END STEP2-5 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-6 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-0 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-7 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-2 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-8 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-4 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-1 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP2-9 - 10000 ===============
[Thu Aug 20 20:16:52 CST 2020]======== END STEP4-3 - 10000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-0 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-4 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-3 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-1 - 300000 ===============
[Thu Aug 20 20:20:30 CST 2020]======== END STEP1-2 - 300000 ===============

从结果回推: 场景e下,完成工作的线程将会从任务队列中取任务并执行,同时此时若新增任务,则先放入任务队列中(有空位);
即当运行状态的线程数等于corePoolSize并且任务队列的大小达到queueCapacity时,此时提交的任务将会被优先执行(新建线程来执行该任务);当任务队列中的堆积任务被处理完后,则线程池的可用线程数将逐步降低至corePoolSize(allowCoreThreadTimeOut=false)

代码学习

你可能感兴趣的:(记JAVA线程池的一次扫盲)