看过前篇的朋友应该知道,作者给我们推荐了阅读的类org.quartz.core.QuartzSchedulerThread
在使用层面,我们跟着教程已经可以满足开发需求,而我们学习源码的目的需求为了提高自己的代码水平,这包括实现原理,设计模式,实践经验等。
QuartzSchedulerThread一眼就能看出它是个Thread,我相信一些问题立马就会涌上心头。立刻记下它们,带着问题看才有效果。
1. 我对Thread类了解吗?
不是很了解,那先查看一遍Thread类的源码,从上到下阅读下,没用过使用一下.
Thread自身有name,priority,status,控制status的各种方法,用来单独执行某个任务,并且有可控的状态来启停,所以QuartzSchedulerThread也是要独立完成某个任务的。
从QuartzSchedulerThread的注释里可以看出它是用来负责触发触发器Trigger的,run方法里有个while循环不断的检查是否有合适的触发器。
2. Thread源码里的name是char[]而不是String,这里System.arraycopy的损耗值得吗?
查阅一番后,这是个遗留问题,据说有些JVM依赖char[] name,有待考证,毕竟很多人说都找不到JVM里调用name的代码。
3. 通读完QuartzSchedulerThread,你会发现,这个类自身就是一把锁加一堆状态变量。而ThreadExecutor也只有两个方法initialize和execute,Quartz自身只是实现了execute,只有一行thread.start()。initialize只是用来给我们这种第三方来实现添加一些定制的逻辑。其实这里有两点问题,一个是initialize的必要性,个人感觉可以去掉,二是只是启动一下线程,没必要在写个ThreadExecute,毕竟它也没用到多线程或者线程池,ThreadExecute和锁应该都可以去掉,代码里也没注释为什么这么用,但是加锁了,就说明有并发的需求吧,后期我们魔改的时候可以把锁去掉,看看会出什么幺蛾子,还可以尝试ReentrantLock来替换monitor lock。
4. 我们注意到ThreadExecutor是专门放在spi包下的,什么是spi呢?
SPI = Service Provider Interface,可以通过类的全限定名称,定义在配置文件或者别的地方,通过反射来实例化。这个可以学习下,可以方便项目达到可扩展性。
问题:实例化对象的方式有哪些
new Operator
Class.newInstance() 注Class.forName只是用来找Class的,不做实例化,不然Class也不用再写个newInstance()了,对吧。
Constructor newInstance()
Object.clone()
Object Serialization and Deserialization
看到这里我们似乎又没有了头绪,就这?
那我们再回头看看作者的推荐引申org.quartz.spi.JobStore.java, org.quartz.spi.ThreadPool.java,org.quartz.core.JobRunShell.java,这里大家可以自行展开,我选择了ThreadPool,因为JobStore是将job写入内存和数据库的两种实现,啃起来一定有点复杂,所以Job相关的先不看,ThreadPool就有点意思了,我们刚看了个Thread却没用ThreadPool,那这个ThreadPool是做什么的呢?
正式切入Quartz的ThreadPool,我们第一反应就是java自带了ThreadPoolExecutor,自己干嘛又写了一套,Quartz的ThreadPool有哪些特性是ThreadPoolExecutor所没有的?
1. Quartz ThreadPool是什么,如何实现的?
ThreadPool是用来服务QuartzScheduler的,ThreadPool维护了一组线程,已达到复用的目的,将新建的Thread存放在LinkedList里,另外还有两个LinkedList用来放availableThreads和busyThreads,所有的add/remove都只是LinkedList节点的add/remove,无需新建,删除任何Thread。
2. Quartz ThreadPool有什么好处和坏处?
好处就是复用,节省资源,节省新建,销毁线程的消耗,可能会挤兑其他进程的资源。
3. ThreadPool的实现也有很多,干嘛自己写一套呢,这样设计的目的是什么?Quartz的ThreadPool和常规的Java ThreadPoolExecutor的区别?
首先二者的实现方式区别很大,ThreadPool只是一个fixed pool,ThreadPoolExecutor相对全面。
Quartz是三个LikedList维护Theads,JDK是一个HashSet,一个BlockingQueue。
抛开线程组的维护,Quartz的特性应该就是blockForAvailableThreads这个方法了,它是目前没有被使用的线程,但是ThreadPoolExecutor只能获得当前拥有的线程数,而不能获取当前可以使用的。如果要在ThreadPoolExecutor里获得这样的AvailableThreads,首先定义一个fixed pool,比如4,然后prestartAllCoreThreads,然后execute一个线程,然后从workQueue里获取remainingCapacity就是3,就是可以使用的Threads了,这里还必须是ArrayBlockingQueue,LinkedBlockingQueue最大可以是Integer.MAX_VALUE,不符合模拟的条件。
Quartz这样做肯定是为了符合自身的业务需求,我们可以尝试使用ThreadPoolExecutor的fixed pool来替换其中的实现,看看能否替换。或者试试自己手动实现。
动手是最后的学习模式,下一步就是改造quartz-core。