# 《并发设计模式》第17章-两阶段终止模式-两阶段终止模式在线程池中的应用

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:了解两阶段终止模式的使用场景,重点掌握两阶段终止模式在线程池中的应用,掌握线程池优雅关闭的流程,重点理解两阶段终止模式的核心原理,并能够结合自身项目实际场景思考如何将两阶段终止模式灵活应用到自身实际项目中。

大家好,我是冰河~~

两阶段终止模式在JDK中典型的应用场景就是停止线程池内部的工作线程,线程池中会有多个工作线程来执行任务,只要线程池中还有工作线程在执行,线程池就不会关闭。只有线程池中所有的线程都退出了,线程池才会关闭。

当外部程序调用ThreadPoolExecutor实例的shutdown()方法关闭线程池时,ThreadPoolExecutor实例并不会直接退出,而是会先将其运行的状态设置为SHUTDOWN。ThreadPoolExecutor实例内部维护的工作线程会在run()方法中判断ThreadPoolExecutor实例的运行状态。如果ThreadPoolExecutor实例的运行状态为SHUTDOWN,则工作线程就会一直获取工作队列中的任务并执行,直到工作队列中的任务为空时,工作线程再停止并退出。所以,调用shutdown()方法关闭线程池时,使用了两阶段终止模式,将整个关闭线程池的过程分为了两个阶段:

(1)准备阶段:将ThreadPoolExecutor实例的状态设置为SHUTDOWN。

(2)执行阶段:工作线程继续获取工作队列中的任务并执行,直到工作队列为空时,再终止线程。

# 一、故事背景

小菜在学习了两阶段终止模式的知识后,了解了什么是两阶段终止模式,并且在老王的指导下最终使用两阶段终止模式完成了监控报警系统中,上报告警信息的功能开发,再次得到了公司的一致认可(这小伙子真特么不错!)。

完成监控报警系统的开发之后,小菜就在思考像两阶段终止模式这么优秀的并发设计模式,Java本身会不会已经提供了典型的使用场景代码呢?想来想去,小菜想到了线程池:线程池在执行任务的过程中,是由内部多个工作线程来执行任务的,那线程池关闭时,会不会使用了两阶段终止模式呢?

想到这里,小菜便迫不及待的打开了线程池的源码进行查看,这一看不要紧,直接阅读线程池的源码,绕来绕去,直接把小菜绕晕了,什么两阶段终止模式啊,根本看不懂线程池的源码。尽管小菜尝试多次重新梳理思路后再来分析线程池的源码,可最后还是迷失在线程池的执行逻辑里。

思来想去,小菜还是决定寻求老王的帮助,要不说老王是个热心肠呢?老王一听小菜经过自己的思考,觉得线程池应该使用了两阶段终止模式,老王还是比较高兴的,毕竟现在积极上进,主动思考和总结的年轻人已经不多了,小菜的这股劲头儿,像极了刚参加工作时候的老王,所以,老王还是很耐心的给小菜讲解了线程池相关的知识,也认真的分析了线程池采用两阶段终止模式优雅退出的源码流程。

# 二、探索线程池执行任务的核心流程

老王首先给小菜认真分析了线程池执行任务的核心流程,细致到什么程度呢?不仅分析了线程池执行任务的核心流程,还为小菜分析了线程池中的拒绝策略。

# 2.1 执行任务的流程

ThreadPoolExecutor是Java线程池中最核心的类之一,它能够保证线程池按照正常的业务逻辑执行任务,并通过原子方式更新线程池每个阶段的状态。

ThreadPoolExecutor类中存在一个workers工作线程集合,用户可以向线程池中添加需要执行的任务,workers集合中的工作线程可以直接执行任务,或者从任务队列中获取任务后执行。ThreadPoolExecutor类中提供了整个线程池从创建到执行任务,再到消亡的整个流程方法。

线程池执行任务的核心流程可以简化成如图17-1所示。


可以看到,当向线程池中提交任务时,线程池执行任务的流程如下所示。

(1)向线程池提交任务时,首先会判断线程池中的线程数是否已经达到corePoolSize,如果线程池中的线程数未达到corePoolSize,则直接创建新线程执行任务。否则,进入步骤(2)。

(2)判断线程池中的工作队列是否已满,如果线程池中的工作队列未满,则将任务添加到队列中等待执行。否则,进入步骤(3)。

(3)判断线程池中的线程数是否已经达到maximumPoolSize,如果线程池中的线程数未达到maximumPoolSize,则直接创建新线程执行任务。否则,进入步骤(4)。

(4)执行拒绝策略。

# 2.2 拒绝策略

# 查看全文

加入冰河技术 (opens new window)知识星球,解锁完整技术文章与完整代码