文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

线程池中线程是如何保活和回收的

2024-11-29 18:29

关注

这个问题对于看过线程池源码的同学应该已经知道答案了,没有看过源码的也不要慌,现在我们就一起看一下线程池线程的保活策略。

一、线程池中在哪执行任务

首先进入ThreadPoolExecutor的execute方法。

在execute方法内部有三处调用addWorker()方法的位置,进入到addWorker() 方法中,可以看到有这样一行代码new Worker(firstTask) 。

而 Worker类又实现了 Runnable,所以线程池中的线程也就是Worker,而执行就在run()方法内部,我们只需要找到Worker类中的run方法即可。

Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        
        public void run() {
            runWorker(this);
        }

run 方法内部调用了 runWorker,秘密就在runWorker方法内部。

所以线程池中线程最终就在runWorker方法中执行的。

二、getTask 获取任务方法

在上面ThreadPoolExecutor的execute方法中,有一步是往阻塞队列中放入任务(workQueue.offer(command))。

上面我们找到了线程池中线程执行任务的地方,那么我们看看是从哪读取的任务?

runWorker方法中有一行代码task = getTask(),此处就是从阻塞队列中获取任务的代码,让我们看一下 getTask() 的内部实现。

核心代码就是

Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

根据timed 的状态判断,当工作线程数量大于核心线程数量时调用poll方法获取任务,当工作线程数量小于和核心线程数量时调用take方法。

对于阻塞队列的介绍如下:

对于非核心线程,当线程池中的线程数量超过核心线程数量且空闲时间超过keepAliveTime时,非核心线程会被回收。

通过这种方式,线程在没有任务时就阻塞在队列上,从而实现保活。

上面我们知道了非核心线程的保活策略,那么对于核心线程又是如何保活的呢?

三、核心线程如何实现的保活

在线程池中核心线程默认是不会被回收的。

不过我们可以通过设置allowCoreThreadTimeOut来实现,getTask方法中timed的校验。

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

所以当allowCoreThreadTimeOut 设置为true时,核心线程在没有任务可以执行时会使用take方法进行阻塞,直到获取到任务位置,而不是因为没有任务就被销毁,从而实现的线程保活。

任务执行的过程是无法保证不出问题的,不管是核心线程还是非核心线程,当线程中出现异常之后,线程池是如何处理的呢?

四、线程异常之后如何保活

继续回到runWorker方法中,其中task.run()是真正执行任务的地方,当此处发生异常之后,有try catch。

所以当任务执行异常之后,会依次执三个finally块中的代码。

再看processWorkerExit() 方法之前,我们先看一个参数completedAbruptly。

completedAbruptly 代表是否是异常退出的,默认是true代表异常退出。

在runWorker方法中,如果线程任务是正常执行完成的,会在最后修改该值。

由于我们是异常线程,所以代码肯定不会走到这,直接走到finally中的processWorkerExit方法。

在processWorkerExit方法中,它会根据completedAbruptly的值来调整线程池中的工作线程数量,从工作线程集合中移除该线程,并根据线程池的状态和工作线程数量决定是否需要添加新线程。

!completedAbruptly 判断工作线程是不是异常退出的,如果不是异常退出的计算最小线程数量。

如果允许核心线程回收allowCoreThreadTimeOut=true,min为0。

如果min为0 且工作队列不为空! workQueue.isEmpty(),min为1。

如果当前工作线程workerCountOf(c)大于等于这个最小的线程数量min,直接返回。

如果小于这个最小的工作线程数量min,调用addWorker。

此处addWorker 的触发条件就是当线程池的状态小于STOP 也就是线程池还在运行runStateLessThan(c, STOP)时且不满足上述不需要添加新线程的判断。

当上述条件满足的时候,则调用addWorker(null,false)添加一个新的工作线程,因为传入的参数Runnable为null,所以这个新线程会从任务队列中继续读取任务来执行。

最后总结一下,当线程异常之后,按照正常情况来说线程就直接消失了,但是通过processWorkerExit方法的补救,增加了一个新的线程,保证线程池的运行。

五、总结

线程池中的线程分为核心线程与非核心线程。

核心线程默认不回收,可以通过设置allowCoreThreadTimeOut为true 来回收。

非核心线程在获取任务为空且空闲时间超过一定时间之后进行回收。

线程池的保活策略通过阻塞队列的阻塞特性实现,poll 方法实现可以指定超时时间的阻塞,take 方法实现阻塞直到获取到任务。

当线程异常之后,通过新增线程的方式实现线程的补救,保证线程池的运行。

来源:醉鱼Java内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯