quartz框架(六)-ThreadPool

本篇博文,博主将介绍Quartz框架中ThreadPool线程池相关的内容。线程池顾名思义,就是一个可以帮助我们来进行线程资源管理的对象。在web开发中,常见的就有数据库连接池,http连接池,redis连接池等。在看这篇文章之前,读者需要先具备一定的多线程和锁的知识,如使用wait和notify方法,实现生产者和消费者功能。

quartz框架中的ThreadPool接口定义如下,博主在相应的接口方法上进行了注释。

public interface ThreadPool {

    //将runnable接口放入到Thread中执行
    boolean runInThread(Runnable runnable);

   //阻塞获取可用线程数
    int blockForAvailableThreads();

   //线程初始化方法
    void initialize() throws SchedulerConfigException;

   //关闭线程
    void shutdown(boolean waitForJobsToComplete);

    //获取线程池大小
    int getPoolSize();

   //设置实例id
    void setInstanceId(String schedInstId);

    //设置实例名称
    void setInstanceName(String schedName);
}

在介绍SimpleThreadPool之前,博主先讲解一下WorkThread。WorkThread继承了Thread类,因此它是一个可以被运行的线程。它会阻塞式的接收线程池分配的任务,然后执行对应的任务。

工作者线程启动之后,在一个while循环里面执行业务逻辑。while循环的退出条件是线程是否关闭(run == false)。先去获取任务锁(lock对象),如果没有关闭且当前runnale方法为空,说明此时没有需要执行的任务。线程会在这里等待500毫秒(wait 500)。当有任务投递给当前线程时,才会唤醒工作者线程,继续执行当前方法。

如果任务不为空的话,执行runnable接口。执行完之后获取lock锁,防止并发冲突。获取到锁之后,设置runnable接口为null。如果只是只执行一次的任务,通过持有的threadPool引用,去获取下一次任务允许的锁,获取到下一次任务允许的锁之后,那么就将自己从busyWorks中移除。否则就从busyWorkers中移除,然后添加到avaliableWorkers。

内部的run(Runnable newRunnable)方法:先去获取内部对象lock的锁,获取成功后先判断内部的runnable是否为空。如果不为空的话说明该线程还是处于繁忙状态,抛出异常。如果为空的话,则设置为传递进来的newRunnable,并且唤醒所有lock等待队列中的对象(提前让这些对象结束等待,提高工作线程的响应速度)。

设置内部成员变量run为false,也就是任务执行完之后不再循环等待下一个可以运行的任务,结束线程的run方法后,线程会进入terminate状态。

在quartz默认的配置文件中,使用的是SimpleThreadPool这个线程池,并且指定了线程池的个数为10。从源码中,我们也可以看到SimpleThreadPool是一个线程大小固定的线程池。代码如下所示:

public SimpleThreadPool(int threadCount, int threadPriority) {
        setThreadCount(threadCount);
        setThreadPriority(threadPriority);
 }

在QuartzScheduler对象进行初始化的时候,就会创建对应的线程池,并且调用对应的initialize方法。initialize方法主要就是预先创建对应个数的工作者线程,并且将它们添加到workers和availWorkers集合中,并且循环调用每个workThread的start方法。

先获取到下一次运行的任务锁(nextRunnableLock),防止这时候的空闲线程集合availWorkers发生变化。循环判断如果此时的availWorkers小于1,或者此时有任务进行等待分配(handoffPending为true),并且此时线程池没有关闭(isShutdown为false),那么就进行等待(wait 500),直到前面的条件都不满足为止才退出循环。

如果此时获取到的空闲线程数大于等于1,则说明现在可以把对应个数的任务交给线程池进行分配执行。

先获取到可以允许下一个任务的锁(nextRunnableLock),设置handoffPending为true,handoffPending表示当前有任务在等待线程池分配任务。接着阻塞判断是否存在空闲线程可以获取(一般情况下,繁忙线程执行完后会将自己添加到空闲线程集合中)或者 线程池是否将要被关闭(isShutdown为false)。

如果没有关闭线程池的情况下,直接从空闲线程集合中拿到第一个线程,并把它从空闲集合中移除。然后将这个空闲线程添加到繁忙线程集合中,接着执行workThread的投递方法(run方法)。

如果关闭线程池将要被关闭的情况下,直接new出一个线程(此时这个WorkThread的runOnce属性为true)去执行这个任务,不再等待有空闲线程去执行,这样可以减少线程池的shutdown时间。并将此线程添加到繁忙线程集合中,添加到工作者集合中。

最后通知等待下一次允许任务锁的线程,设置handoffPending为false。

整体来说,我们关闭线程池时,一个需要停止上游线程(quartzScheduleThread)给他(thradPool)分配任务,另一个需要关闭掉工作池中现有的任务。关闭线程池的方法有一个waitForJobsToComplete的属性,waitForJobsToComplete表示线程池关闭是否需要等到运行中的任务执行完毕。

先获取nextRunnableLock,设置线程池为关闭状态(shutdown),并循环调用workers集合中thread的shutdown方法(不让工作者线程再循环执行),然后移除availWorkers中的线程。通知阻塞在nextRunnableLock锁上的所有线程。

如果waitForJobsToComplete为true,那么会循环判断busyWorkers的元素个数是否大于0(一般情况下,繁忙集合中的线程结束任务后,会将自己从繁忙集合中移除),如果busyWorkers的元素个数大于0的,调用nextRunnableLock的阻塞方法,让其它方法有时间处理(比如如果此时有任务需要进行分配,可以让线程池把任务分配好)。最后循环调用每个workers中thread的join方法,等待thread死亡。

如果waitForJobsToComplete为false,那么会直接返回。

Original: https://www.cnblogs.com/chenhaoblog/p/15999212.html
Author: 幕友皎敖奔乾
Title: quartz框架(六)-ThreadPool

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/580682/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 线程池使用

    线程池 1.工具类实现 undefined 线程池监控: long activeCount = ((ThreadPoolExecutor)instance).getActiveCo…

    Java 2023年6月9日
    077
  • java poi

    POI and EasyExcel Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java对Microsoft Of…

    Java 2023年6月9日
    069
  • json的String串如何做修改?

    json的String串如何做修改? 1.引入依赖 3.引入 Original: https://www.cnblogs.com/hexf/p/16545945.htmlAutho…

    Java 2023年6月9日
    085
  • Eclipse 使用配置全面讲解

    首次使用的必要设置 1、Eclipse 的安装 下载地址:64位neon版eclipse下载 (阿里云盘下载地址,使用7-zip的自释放压缩,下载后直接双击解压,无法解压的话请下载…

    Java 2023年6月8日
    091
  • 分布式中灰度方案实践

    让请求在导航的服务节上点执行; 一、背景简介 分布式系统中会存在这样的开发场景,不同需求可能涉及到对同一个服务的开发,那么该服务在研发期间就会存在多个版本并行的状态,为了保持不同版…

    Java 2023年6月15日
    063
  • nginx+tomcat 架构 HttpServletRequest.getScheme()获取正确的协议

    问题:通过浏览器输入https://www.mysite.com,后台通过request.getScheme()获取到的确实http而不是https 通过request.getRe…

    Java 2023年5月30日
    073
  • SpringCloud中使用Nacos作为配置中心原理

    使用了是Nacos的自动配置依赖 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config 2.2.5.RELEASE…

    Java 2023年5月30日
    079
  • Spring系列22:Spring AOP 概念与快速入门篇

    本文内容 Spring AOP含义和目标 AOP相关概念 声明式AOP快速入门 编程式创建代理对象 Spring AOP含义和目标 OOP: Object-oriented Pro…

    Java 2023年6月5日
    081
  • 【力扣】525. 连续数组

    给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。 示例 1: 输入: nums = [0,1]输出: 2说明: [0, 1…

    Java 2023年6月8日
    055
  • synchronized锁及其锁升级

    点赞再看,养成习惯,微信搜索「 小大白日志」关注这个搬砖人。 文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。 多线程加锁有两种方式 利用Sychronized关…

    Java 2023年6月8日
    080
  • k8s Ingress-nginx 部署使用

    k8s版本信息:v1.18.0 官方文档中,部署只要简单的执行一个yaml https://github.com/kubernetes/ingress-nginx/blob/ngi…

    Java 2023年5月30日
    0103
  • 整合SSM框架

    具体步骤 准备工作 创建一个maven项目改成web工程 WEB-INF下建一个jsp文件夹 在pox.xml先导入依赖和静态资源过滤 junit junit 4.12 mysql…

    Java 2023年6月7日
    089
  • MySQL性能优化的5个维度

    面试官如果问你:你会从哪些维度进行MySQL性能优化?你会怎么回答? 所谓的性能优化,一般针对的是MySQL查询的优化。既然是优化查询,我们自然要先知道查询操作要经过哪些环节,然后…

    Java 2023年6月7日
    067
  • Java CAS 原理详解

    背景 在JDK 5之前Java语言是靠 synchronized 关键字保证同步的,这会导致有锁。锁机制存在以下问题: 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延…

    Java 2023年5月29日
    070
  • java 线程、进程及相关知识点 《笔记一》

    一、线程、进程 线程,就是是进程的一个单位,程序最小执行单位。 进程,就是一个执行中的应用程序;由此可见,进程是由很多线程组成的; 线程生命周期,就是管杀也管埋的过程,生老病死; …

    Java 2023年6月16日
    069
  • Eclipse启动Tomcat后无法访问项目

    Eclipse中的Tomcat可以正常启动,不过发布项目之后,无法访问,包括http://localhost:8080/的小猫页面也无法访问到,报404错误。这是因为Eclipse…

    Java 2023年6月8日
    073
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球