美文网首页
记JAVA线程池的一次扫盲

记JAVA线程池的一次扫盲

作者: LaxChan | 来源:发表于2020-08-20 20:32 被阅读0次

    前言

    某日同一同事探讨线程池的几个关键参数: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<Runnable>(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<taskNum;++i){
                BlockThread bt = new BlockThread(blockTime,key_pre + i);
                exec.execute(bt);
            }
            try {
                Thread.sleep(1 * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            key_pre = "STEP2-";
            taskNum = 10;
            blockTime = 10;
            //STEP2. 继续提交10个阻塞时间未10s的任务
            for(int i =0;i<taskNum;++i){
                BlockThread bt = new BlockThread(blockTime,key_pre + i);
                exec.execute(bt);
            }
            try {
                Thread.sleep(1 * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            key_pre = "STEP3-";
            taskNum = 1;
            blockTime = 10;
            //STEP3. 继续提交1个阻塞时间为10s的任务
            for(int i =0;i<taskNum;++i){
                BlockThread bt = new BlockThread(blockTime,key_pre + i);
                exec.execute(bt);
            }
            //SLEEP 60s
            try {
                Thread.sleep(60 * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            key_pre = "STEP4-";
            taskNum = 20;
            blockTime = 10;
            //STEP4. 继续提交20个,阻塞时间未10s的任务
            for(int i =0;i<taskNum;++i){
                BlockThread bt = new BlockThread(blockTime,key_pre + i);
                exec.execute(bt);
            }
            System.out.println("------------ END --------------");
        }
    }
    
    • 查看输出结果并分析
    /** 初始提交任务  **/
    [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线程池的一次扫盲

          本文链接:https://www.haomeiwen.com/subject/uuuejktx.html