线程池工作原理


一、什么是线程池?

具体可参考《线程池学习笔记》

二、举个栗子(用比喻的方式解释线程池工作原理)

1、相关名词

线程池 开发部门
核心线程 正式员工(长期存在)
非核心线程 非正式员工(如外包员工,需要才用到)
执行任务 开发需求
阻塞队列 开发需求TODO List(排不下期,需要排队等有人力开发)
空闲时间(非核心线程存活时间) 非正式员工待命时间(没需求做之后仍空闲停留的时间)
拒绝策略 需求饱和后,接下来的需求需要评估看看是不做了还是谁提谁做等

2、开始讲故事

线程池

  • 有一天,老板想要开发一个开发系统A,但是自己团队并没有开发人员(线程)
  • 所以老板去58同城找了开发A(线程A)完成任务
  • 第二天老板又想开发一个开发系统B,又只能去58同城找了**开发B(线程B)完成任务
  • 久而久之,老板发现这样很麻烦,每次都要重复去找人开发,所以老板自己招了三个程序员A、B、C(线程A、B、C),成立了一个开发团队(线程池),后面的需求就可以内部分配完成了

核心线程

  • 也就是正式员工,长期稳定做需求的

阻塞队列

  • 当产品下发了10个需求,但是开发只有3个,一人领走一个需求,那么剩下的7个需求就先放到TODO List里,等待开发完成手头需求后再来这里领新的任务
  • 阻塞队列可以是有上限的也可以是没有上限的

非核心线程

  • 也就是非正式员工,需求太多做不过来(阻塞队列满了)才需要用到
  • 为什么不招多几个正式员工呢?因为开销大,而且只是应对突然需求过多的场景,如果需求不多的话那么成本(性能)就会有所影响

空闲时间

  • 老板认为非正式员工是能很好解决需求过多的问题,但如果需求变少或者没有的话,那么这一块会是需要考虑的成本问题
  • 所以老板决定,等需求TODO List做完后,就让非正式员工遣回外包公司
  • 但是又出现一个问题:如果遣回第二天又有很多需求做不完怎么办?来来回回也是很麻烦的?
  • 所以老板做出了取舍,等需求TODO List做完后,让非正式员工呆个3天5天再遣回外包公司,防止这段时间又出现太多需求做不完的情况

拒绝策略

  • 背景:有了非正式员工,可以很好解决需求饱和问题,但是如果需求实在多到做不完怎么办呢?
  • 正式员工非正式员工一起开发需求也做不完(需求TODO List满了),就应该对这些塞不下的需求做拒绝策略
  • 拒绝策略有几种:丢弃该需求抛异常,说加不了了把需求TODO List最早的任务丢掉让提需求的人自己开发

总结

  • 要做需求(任务)就需要有开发人员(核心线程)
  • 开发人员(线程)做不过来了就先放入需求TODO List等待
  • 如果需求TODO List满了,则多招几个外包开发人员(非核心线程)
  • 但是外包开发人员(非核心线程)如果在没有需求的情况下,过一段时间(空闲时间)就要遣回外包公司
  • 如果需求TODO List外包开发人员(非核心线程)都满了,就要学会拒绝(拒绝策略)

3、流程图

对应源码为:

public class ThreadPoolExecutor extends AbstractExecutorService {
    // ...
    public public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        // 判断工作线程数是否小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            // 如果是,则创建新的工作线程
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果不是,则加入到阻塞队列;同时检查线程池是否运行中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次检查线程池是否运行中,如果不是的话则移除任务并触发拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果工作线程数为0,则创建线程(但不执行,因为上面已经offer到队列里了)
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果加入阻塞队列失败,则创建非核心线程执行
        else if (!addWorker(command, false))
            // 如果非核心线程已满,则触发拒绝策略
            reject(command);
    }
    
    //...
}

文章作者: GaryLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GaryLee !
  目录