加入收藏 | 设为首页 | 会员中心 | 我要投稿 宿州站长网 (https://www.0557zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

当面试官问Java线程池时,你应该了解些什么?

发布时间:2021-12-07 15:24:14 所属栏目:教程 来源:互联网
导读:根据江门站长网 Www.0750Zz.Com报道 Java面试中,线程池也算是一个高频的问题,其实就JDK源码来看线程池这一块的实现代码应该算是写的清晰易懂的,通过这篇文章,我们就来盘点一下线程池的知识点。 本文基于JDK1.8源码进行分析 首先看下线程池构造函数: pub
 
根据江门站长网 Www.0750Zz.Com报道

Java面试中,线程池也算是一个高频的问题,其实就JDK源码来看线程池这一块的实现代码应该算是写的清晰易懂的,通过这篇文章,我们就来盘点一下线程池的知识点。
 
本文基于JDK1.8源码进行分析
 
首先看下线程池构造函数:
 
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //忽略赋值与校验逻辑
    }
构造参数比较多,一个一个说下:
 
corePoolSize线程池中的核心线程数
maximumPoolSize线程池中的最大线程数
keepAliveTime线程池中的线程存活时间(准确来说应该是没有任务执行时的回收时间,后面会分析)
unit时间单位
workQueue来不及执行的任务存放的阻塞队列
threadFactory新建woker线程(注意不是我们提交的任务)是进行一些属性设置,比如线程名,优先级等等,有默认实现。
handler 任务拒绝策略,当运行线程数已达到maximumPoolSize,队列也已经装满时会调用该参数拒绝任务,有默认实现。
当我们向线程池提交任务时,通常使用execute方法,接下来就先从该方法开始分析。
在分析execute代码之前,需要先说明下,我们都知道线程池是维护了一批线程来处理用户提交的任务,达到线程复用的目的,线程池维护的这批线程被封装成了Worker。
 
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //JDK8的源码中,线程池本身的状态跟worker数量使用同一个变量ctl来维护
        int c = ctl.get();
        //通过位运算得出当然线程池中的worker数量与构造参数corePoolSize进行比较
        if (workerCountOf(c) < corePoolSize) {
            //如果小于corePoolSize,则直接新增一个worker,并把当然用户提交的任务command作为参数,如果成功则返回。
            if (addWorker(command, true))
                return;
            //如果失败,则获取最新的线程池数据
            c = ctl.get();
        }
        //如果线程池仍在运行,则把任务放到阻塞队列中等待执行。
        if (isRunning(c) && workQueue.offer(command)) {
            //这里的recheck思路是为了处理并发问题
            int recheck = ctl.get();
            //当任务成功放入队列时,如果recheck发现线程池已经不再运行了则从队列中把任务删除
            if (! isRunning(recheck) && remove(command))
                //删除成功以后,会调用构造参数传入的拒绝策略。
                reject(command);
             //如果worker的数量为0(此时队列中可能有任务没有执行),则新建一个worker(由于此时新建woker的目的是执行队列中堆积的任务,
             //因此入参没有执行任务,详细逻辑后面会详细分析addWorker方法)。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //如果前面的新增woker,放入队列都失败,则会继续新增worker,此时线程池的状态是woker数量达到corePoolSize,阻塞队列任务已满
        //只能基于maximumPoolSize参数新建woker
        else if (!addWorker(command, false))
            //如果基于maximumPoolSize新建woker失败,此时是线程池中线程数已达到上限,队列已满,则调用构造参数中传入的拒绝策略
            reject(command);
    }
源码里我增加了很多注释,需要多读几遍才能完全理解,总结一下用户向线程池提交任务以后,线程池的执行逻辑:
 
如果当前woker数量小于corePoolSize,则新建一个woker并把当前任务分配给该woker线程,成功则返回。
如果第一步失败,则尝试把任务放入阻塞队列,如果成功则返回。
如果第二步失败,则判断如果当前woker数量小于maximumPoolSize,则新建一个woker并把当前任务分配给该woker线程,成功则返回。
如果第三步失败,则调用拒绝策略处理该任务。
从execute的源码可以看出addWorker方法是重中之重,马上来看下它的实现。
addWorker方法:
 
private boolean addWorker(Runnable firstTask, boolean core) {
        //这里有一段基于CAS+死循环实现的关于线程池状态,线程数量的校验与更新逻辑就先忽略了,重点看主流程。
        //...
 
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
             //把指定任务作为参数新建一个worker线程
            w = new Worker(firstTask);
            //这里是重点,咋一看,一定以为w.thread就是我们传入的firstTask
            //其实是通过线程池构造函数参数threadFactory生成的woker对象
            //也就是说这个变量t就是代表woker线程。绝对不是用户提交的线程任务firstTask!!!
            final Thread t = w.thread;

(编辑:宿州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章