1.ScheduledExecutorService的接口介绍
package java.util.concurrent;
public interface ScheduledExecutorService extends ExecutorService {
//单次执行,在指定延时delay后运行command任务
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
//单次执行,在指定延时delay后运行callable任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
//固定频率执行任务
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period, TimeUnit unit);
//固定延时重复执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
}
(1)ScheduledExecutorService接口中包含四个方法进行任务的定时调度,解释分别如上。
(2)对于 固定延时 的任务时在任务执行后开始计算的,第一次任务是在initialDelay后,第二次任务是在第一次任务执行完以后,再加上delay。
2.ScheduledExecutorService的实现类:ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor的定义
//继承线程池、实现ScheduledExecutorService接口
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
ScheduledThreadPoolExecutor 的构造方法:跟线程的构造差不多,不多做介绍
public ScheduledThreadPoolExecutor(int corePoolSize);
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory);
public ScheduledThreadPoolExecutor(int corePoolSize,RejectedExecutionHandler handler);
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) ;
说明:并发包下的:Executors类也提供了创建ScheduledExecutorService的方法,但是阿里巴巴的开发规范中不建议这么用,还是显示的创建线程池会比较好
java.util.concurrent.Executors#newSingleThreadScheduledExecutor();
java.util.concurrent.Executors#newSingleThreadScheduledExecutor(java.util.concurrent.ThreadFactory);
java.util.concurrent.Executors#newScheduledThreadPool(int);
java.util.concurrent.Executors#newScheduledThreadPool(int, java.util.concurrent.ThreadFactory);
3.ScheduledExecutorService定时调度的一些实例分析
任务一:
/**
* 任务一:
*/
public class Task1 implements Runnable{
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("[当前线程是:"+threadName+",执行定时任务1:"+System.currentTimeMillis()+"]");
}
}
任务二:
/**
* 任务2:
*/
public class Task2 implements Runnable{
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("[当前线程是:"+threadName+",执行定时任务1:"+System.currentTimeMillis()+"]");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试场景一:
Task1 task1=new Task1();
Task2 task2=new Task2();
ScheduledExecutorService service=new ScheduledThreadPoolExecutor(10);
//重复执行,启动延迟一秒执行,每隔一秒反复执行任务一
service.scheduleAtFixedRate(task1,1,1, TimeUnit.SECONDS);
//启动后,延迟两秒在执行任务二,但是任务一并没有堵塞
service.schedule(task2,2,TimeUnit.SECONDS);
现象:
(1)任务一:重复执行,启动延迟一秒执行,每隔一秒反复执行。
(2)任务二:启动延迟两秒后执行,同时任务内线程堵塞10秒中。
(3)任务一的执行并不会被任务二的中的Thread.sleep(10000)给堵塞,因为ScheduledExecutorService的任务调度执行是多线程的。
测试场景二:
咱们修改一下任务二:在任务二中抛出 异常
任务一还是跟上面一样
public class Task2 implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("[当前线程是:"+threadName+",执行定时任务2:"+System.currentTimeMillis()+"]");
throw new RuntimeException("我们尝试在 任务二 中抛出一个异常");
}
}
测试代码:任务一和任务二都是反复执行的任务类型
Task1 task1=new Task1();
Task2 task2=new Task2();
ScheduledExecutorService service=new ScheduledThreadPoolExecutor(10);
service.scheduleAtFixedRate(task1,1,1, TimeUnit.SECONDS);
service.scheduleAtFixedRate(task2,3,1, TimeUnit.SECONDS);
现象:
(1)任务二抛出了异常,任务二将不再执行(停止),但是异常并不会在调用方抛出,所以需要在任务二的run方法中处理异常。
(2)任务一并不受影响。
4.总结(主要是与Timer的不同)
(1)ScheduledExecutorService 使用的是线程池实现,可以有多个线程执行任务。
(2)它在任务执行后再设置下次执行的时间,对于固定延时的任务更为合理。
(3)一个定时任务的异常并不会影响其他定时任务的执行,但是当一个定时任务发生异常的时候,哪怕是重复执行的任务都不会再被调度。