记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<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)

代码学习

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342