定时方案一直使用Timer,因为调用的不频繁,对时间把控也没有那么绝对的严格,所以也没发现问题。
说一下需求:
以10s为一个周期,每隔1s钟检查一下服务器是否回复,10s钟时仍没有回复,则进入额外20s的超时判断,第30s上仍没回复则提示超时。
使用Timer时,以10s为单位启停一次,实际上是0延迟1秒为周期,用全局变量做计数器去统计10s,结果整个周期最大的时候竟然出现了2s的误差,这是不能接受的。
于是考虑使用JDK5的多线程包ScheduledExecutorService
网上的资料多,而且跟官网范例区别不大,基本只演示了怎么启动任务,怎么停止所有任务,可是我要控制单个任务的停止,资料就很少。
参考了上面那个帖子之后,使用idea做了测试
static int time_1 = 0,time_2=0;
static Future futureDelay,futureFixedDelay;
public static void main(String[] args){
//定时功能测试
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
System.out.println("before"+System.currentTimeMillis());
// schedule:延迟多长时间之后只执行一次,执行完自动结束;
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("schedule single"+System.currentTimeMillis());
}
},0, TimeUnit.MILLISECONDS);
// scheduledAtFixedRate:延迟指定时间后执行一次,之后按照固定的时长周期执行;
time_1=0;
time_2=0;
futureDelay = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try{
time_1++;
System.out.println("schedule delay"+time_1+" mills:"+System.currentTimeMillis());
if(time_1>=10){
futureDelay.cancel(false);
System.out.println("schedule delay finished "+System.currentTimeMillis());
}
}catch (Exception e){
e.printStackTrace();
System.out.println("schedule Exception:"+e.getMessage());
}
}
},0,1000,TimeUnit.MILLISECONDS);
// scheduledWithFixedDelay:延迟指定时间后执行一次,之后按照:上一次任务执行时长 + 周期的时长 的时间去周期执行;
futureFixedDelay=scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try{
time_2++;
System.out.println("schedule FixedDelay"+time_2+" mills:"+System.currentTimeMillis());
if(time_2>=10){
futureFixedDelay.cancel(false);
System.out.println("schedule FixedDelay finished " + System.currentTimeMillis()+" size:"+scheduledExecutorService.shutdownNow().size());
}
}catch (Exception e){
e.printStackTrace();
System.out.println("schedule Exception:"+e.getMessage());
}
}
},5000,3000,TimeUnit.MILLISECONDS);
}
我这里只放了测试代码,添加了三个定时任务,分别测试了
Executors.newScheduledThreadPool(1)
Executors.newScheduledThreadPool(2)
Executors.newScheduledThreadPool(3)
Executors.newSingleThreadScheduledExecutor()
以上四种情况感觉区别不是很大,误差在5毫秒以内,我最后使用Executors.newSingleThreadScheduledExecutor()感觉就可以了
实际上ScheduledExecutorService 有五个函数可以构建对象,实际上最常用的是newScheduledThreadPool(N)和newSingleThreadScheduledExecutor()
1)newSingleThreadScheduledExecutor
:new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1))
2)newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
:new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1, threadFactory));
3)newScheduledThreadPool(int corePoolSize)
:new ScheduledThreadPoolExecutor(corePoolSize);
4)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
:new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
5)unconfigurableScheduledExecutorService(ScheduledExecutorService executor)
:new DelegatedScheduledExecutorService(executor);
之后测试了重复开线程
static int time_1 = 0,time_2=0;
static ArrayList<Future> futures;
static Future futureDelay,futureFixedDelay;
static ScheduledExecutorService scheduledExecutorService;
public static void main(String[] args){
futures = new ArrayList<>();
//定时功能测试
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
System.out.println("before"+System.currentTimeMillis());
// scheduledAtFixedRate:延迟指定时间后执行一次,之后按照固定的时长周期执行;
time_1=0;
time_2=1;
futures.add(scheduledExecutorService.scheduleAtFixedRate(getTask("task"+time_2),0,1000,TimeUnit.MILLISECONDS));
// futureDelay = ;
}
private static Runnable getTask(String taskName){
return new Runnable() {
@Override
public void run() {
try{
time_1++;
System.out.println(taskName+" schedule delay"+time_1+" mills:"+System.currentTimeMillis());
if(time_1>=10){
time_2++;
//如果设置true,则表示可以取消正在执行过程中的任务
futures.get(futures.size()-1).cancel(true);
System.out.println(taskName+" finished "+System.currentTimeMillis());
if(time_2<=5) {
time_1=0;
futures.add(scheduledExecutorService.scheduleAtFixedRate(getTask("task"+time_2),0,1000,TimeUnit.MILLISECONDS));
// futureDelay = scheduledExecutorService.scheduleAtFixedRate(getTask("task" + time_2), 0, 1000, TimeUnit.MILLISECONDS);
}
}
}catch (Exception e){
e.printStackTrace();
System.out.println(taskName+" schedule Exception:"+e.getMessage());
}
}
};
}