Java:简述线程池的工作原理

Java:简述线程池的工作原理


文章开始,我们先来看一个简单的例子。

场景:某地的某银行营业厅柜台开了8个窗口,每个窗口里面都坐了一位工作人员;当储蓄用户来到银行大厅后,看见有窗口是空闲的,那么就可以直接前往空闲的窗口去办理业务;慢慢地,到银行办理储蓄业务的人越来越多了,8个窗口都在对外提供服务;这个时候,新到的客户就只能进行排队。

如果新来客户的增长速度远远大于银行工作人员完成一项客户业务的速度,这样客户排队就会越积越多。这时候,银行的大堂经理可能会增开2个临时窗口为排队的客户提供服务。

那知道今天是除夕,过来办理储蓄业务客户越来越多,10(8+2)个窗口也不够用了,这个时候大堂经理可能就要考虑不再接收新客户或规劝(抛弃)现在排队的客户去就近的银行营业厅去办理业务了。

时间到了下午,过来办理储蓄业务的客户越来越少了(可能大家都去准备年夜饭了),大堂经理发现新增的2个临时窗口空闲后,又等待观察了10分钟后,发现空闲依旧,可能就要考虑把之前的2个临时窗口关闭了,只保留之前的8个窗口。

好了,例子讲完了,上述的例子只是对线程池工作过程的一个形象化比喻。下面我们看下ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}

ThreadPoolExecutor构造函数中参数含义如下:

  1. corePoolSize:指定了线程池中的线程数量;指代“上例中的8个窗口”
    maximumPoolSize:指定了线程池中的最大线程数量;指代“上例中的10个窗口(8个正式窗口+2个新增临时窗口)”。
  2. keepAliveTime:当线程池线程数量超过corePoolSize时,多余的空闲线程的存活时间。即:超过corePoolSize的空闲线程,在多长时间内,会被销毁。指代“大堂经理发现新增的2个临时窗口空闲后,又等待观察了10分钟”中的数值10。
  3. unit:keepAliveTime的单位;指代“上例中的10分钟”中的分钟。
    workQueue:任务队列,被提交但尚未被执行的任务;指代“上例中的10个窗口”。
  4. threadFactory:线程工厂,用于创建线程,一般用默认的即可。
  5. handler:拒绝策略。当任务太多来不及处理,如何拒绝任务。

需要注意线程池中有两个核心参数,很容易弄错,我们再辨析一下:

  1. corePoolSize:该值设定好以后,默认情况下并不会在线程池中创建任何线程,而是等待有任务来时才开始创建线程;只有显式调用了prestartAllCoreThreads方法后,线程池才会预先创建corePoolSize个线程。也就是说,默认情况下创建的线程池,线程池中的线程数为0,当有任务来之后,才会创建以线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
  2. maximumPoolSize:它表示线程池中最多能创建多少个线程。
    达到(corePoolSize+1)数目之前的线程并不会被线程池释放销毁,但是从(corePoolSize+1)~ maximumPoolSize之间的创建的线程会被释放销毁。

最后,我们看下ThreadPoolExecutor的执行调度程序源代码,了解线程池的执行过程。

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
        
    int c = ctl.get();
    //利用workerCountOf获取当前线程池的线程总数
    //如果小于corePoolSize,调用addWorker调度线程执行
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    //如果大于corePoolSize,则调用workQueue.offer
    //将任务进入等待队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //如果进入等待队列失败,则将任务提交给线程池
    //如果线程池数目已经达到maximumPoolSize,则执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
 }

源码分析如下:

  1. 任务提交后,如果线程总数小于corePoolSize,则分配新的线程,执行任务;
  2. 任务提交后,如果线程总数大于corePoolSize,则任务准备进入等待队列。如果进入等待队列成功,则等待被执行。如果进入等待队列失败,则将任务提交给线程池。如果线程池数目未达到maximumPoolSize,则分配线程执行;如果线程池数目已经达到maximumPoolSize,则执行拒绝策略。

参考:https://mp.weixin.qq.com/s/VW3f-399hUuZGLHdkKkOJw

©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页