Timer
Timer的核心是Timer和TimerTask,Timer负责设定TimerTask的起始与间隔执行时间,使用者需要建立一个timeTask的继承类,实现run方法,然后将其交给Timer使用即可。
Timer的设计是一个TaskList和一个TaskThread。Timer将接到的任务交给TaskList中,TaskList按照最初执行时间进行排序。TimerThread在创建Timer会成为一个守护线程。这个线程会轮询任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread被唤醒并执行任务。之后Timer更新最近要执行的任务,继续休眠。
缺点:所有任务都是串行的,前一个任务的延迟或异常会影响到后面的任务。
demo:
1 Timer timer = new Timer(); 2 TimeTask task = new TimeTask(){ 3 public void run(){ 4 //具体业务 5 } 6 }; 7 timer.scheduleAtFixedRate(task, new Date(), 1* 30 * 1000l);
ScheduledExecutor
java 5退出了ScheduledExecutor框架, 与timer不同的是,任务执行时是并发的,相互之间不受干扰,(保留意见,不一定非得是并发执行的就好),只有当任务执行时间到的时候,scheduedExecutor才会真正的执行线程,其余时间ScheduledExecutor都在轮询任务的状态。
1 public class ScleduledExecutorTest implements Runnable{ 2 private String jobName = ""; 3 public ScheduledExecutorTest(String jobName){ 4 super(); 5 this.jobName = jobName; 6 } 7 public void run(){ 8 System.out.println("execute " + jobName); 9 } 10 public static void main(String[] args){ 11 ScheduledExecutorService service = Executors.newScheduledThreadPool(10); 12 service.scheduleAtFixRate(new ScheduledExecutorTest("job1"),1,1,TimeUnit.SECONDS); 13 service.scheduleWithFixedDelay( 14 new ScheduledExecutorTest("job2"), 1, 15 1, TimeUnit.SECONDS); 16 } 17 }
注意,能做定时执行的只能是newScheduledThreadPool 这个是专门做调度任务的,其他的线程池特性如下:
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
并且线程池在停止的时候所用的shutdown与shutdownNow是不一样的,shutdown会继续执行并且完成所有未执行的任务,shutdownNow 会清楚所有未执行的任务并且在运行线程上调用interrupt() 。然而interrupt仅仅是在当前线程中打一个停止的标记。还需要加一个判断才能对线程的停止。
Thread.java提供了两个方法:
this.interrupted();
this.isInterrupted();
官方帮助文档对interrupted方法的解释是:
调用后,线程的中断状态被清除,也就是如果线程中断了,第一次返回true,第二次返回false。 并且这是一个静态方法。
isInterrupted不是一个静态方法,也不会清除中断状态。
中断线程通过标记判断,要么抛出异常,要么直接return。
ScheduledExecutor可以与Calendar实现更复杂的调度,比如制定每周的某个时间段执行。
也就是在执行线程中加上了Calandar的判断
Calendar ca = Calendar.getInstance(); int h = ca.get(Calendar.HOUR_OF_DAY); int mi = h*60+ca.get(Calendar.MINUTE); int ss = ca.get(Calendar.SECOND);
通过if进行判断,要注意,调度器的唤醒时间最好是各个线程激活的最大公约数。
Quartz
需要更精准的调度,需要使用Quartz框架了。
public static void main(String args[]){ UpdateJob.task(AutoJob.class,"0/1 * * * * ?" ); }
public class UpdateClientTimer { public static void task(Class cls, String expression) throws SchedulerException { SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); long ctime = System.currentTimeMillis(); JobDetail jobDetail = new JobDetail("detail", "group", cls); CronTrigger cronTrigger = new CronTrigger("cronTrigger", "group"); try { CronExpression cexp = new CronExpression(expression); cronTrigger.setCronExpression(cexp); } catch (Exception e) { e.printStackTrace(); } scheduler.scheduleJob(jobDetail, cronTrigger); scheduler.start(); } }
调度周期是 "0/1 * * * * ?" 更精准的调度需要自行配置。
UpdateClientTimer 大体与官方文档相当。真正执行业务在本例的Autojob中。
public class AutoJob implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { Calendar ca = Calendar.getInstance(); int h = ca.get(Calendar.HOUR_OF_DAY); int mi = h*60+ca.get(Calendar.MINUTE); int ss = ca.get(Calendar.SECOND); if(过滤条件){ } }
Quartz的核心类包括Scheduler,job以及Trigger。其中job负责定义需要执行的任务,trigger负责设置调度策略,Scheduler 将二者组装在一起。
有相关的官方说明文档,可以直接拿demo用。