本文为《Java并发编程的艺术》学习笔记,详情见书本。
作用:
在给定的延迟之后运行任务,或者定期执行任务。(总之和时间有关,要么延迟,要么定期,体现“Scheduled”)。
与Timer比较:
类似于Timer类,但它更加强大和灵活。
因为Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
ScheduledThreadPoolExecutor的运行机制:
DelayQueen是一个无界队列,所以类似的,在这里maximumPoolSize参数没有什么意义。
执行过程:
1、当调用scheduleAtFixedRate()方法或者scheduleWithFixedDelay()时,会向DelayQueen添加一个ScheduleFutureTask(实现了RunnableScheduleFuture接口)。
2、线程池中的线程从DelayQueen中获取ScheduleFutureTask,然后执行任务。
小结:
ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改:
①使用DelayQueen作为任务队列。
②获取任务的方式不同。
③执行周期任务后,增加了额外的处理。
代码实现:
ScheduleFutureTask主要包含三个成员变量:
1、long型成员变量time,表示这个任务将要被执行的具体时间;
2、long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号;
3、long型成员变量period,表示任务执行的间隔周期;
关于DelayQueue:
DelayQueue封装了一个PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序。排序按事件早的任务先被执行。
如果两个线程的time相同,则先被提交的先执行。
执行过程:
1、线程1从DelayQueue中获取已经到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指time大于等于当前时间。
take方法:
①获取lock。
②获取周期任务:
(1)如果PQ为空,当前线程到Condition中等待;否则执行下一条
(2)如果PQ的头元素的time时间比当前时间打,到Condition中等待time时间;否则执行下一条。
(3)获取PQ的头元素;如果PQ不为空,唤醒Condition中等待的所有线程。
③解锁lock。
2、线程1执行这个ScheduledFutureTask。
3、线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。
4、线程1把被修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。