Java 调度器延时存在误差,是否有更精确的调度方法?
由于业务需要业务中需要非常精确的任务调度,例如100毫秒执行一次的业务,但是通过scheduledExecutor.scheduleAtFixedRate;执行的时候会经常出现10ms内的一个时间误差,例如有时候提前几毫秒,有时候推迟几毫秒,偶尔还会出现几十毫秒的差异,因为业务需求是希望差异尽可能的小,所以大家有什么好的办法能解决或者减小这种误差
-
首先,复习一把Java中的时间单位:
** 1毫秒=1ms=1/1000秒,表达方式TimeUnit.MILLISECONDS
- 1微秒=1μs=1/1000毫秒,表达方式TimeUnit.MICROSECONDS
- 1纳秒=1ns=1/1000微秒,表达方式TimeUnit.NANOSECONDS*
然后,我猜测你是由于时间单位直接使用毫秒,没有使用最精确的纳秒导致的,于是我写了一个程序检测:
public class TimeTest { static final long PER_NANO = 1000 * 1000; //每毫秒为1000*1000纳秒 public static void main(String[] args) { final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); final long times[] = new long[100]; //用来存放时间的数组 Worker worker = new Worker(times); //重复任务,延迟100ms开始,间隔100ms final ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(worker, 100, 100, TimeUnit.MILLISECONDS); //延迟任务,延迟10000ms执行。取消重复任务,输出结果 scheduler.schedule(new Runnable() { public void run() { beeperHandle.cancel(true); long max = 0; for (int i = 1; i < times.length; i++) { long diff = times[i] - times[i - 1]; max = Math.max(max, diff); System.out.println(diff); } double maxdiff = ((double) (max) / PER_NANO) - 100; System.out.println("max = " + maxdiff + " ms"); scheduler.shutdown(); } }, 10000 * PER_NANO, TimeUnit.NANOSECONDS); } //为了防止IO占用时间,把时间先存入数组,最后再一次输出 static class Worker implements Runnable { private static int number; //序号 private final long times[]; //时间数组 Worker(long times[]) { this.times = times; } @Override public void run() { times[number++] = System.nanoTime(); } } }
多次运行结果如下(单位为纳秒,省略了前面的输出):
max = 0.9890390000000053 ms max = 0.984603000000007 ms max = 0.991429999999994 ms
我发现误差没有超过2ms的。
我修改了时间单位为纳秒:
final ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(worker, 100 * 1000 * 1000, 100 * 1000 * 1000, TimeUnit.NANOSECONDS);
多次运行结果如下(单位为纳秒,省略了前面的输出):
max = 0.993819000000002 ms max = 0.9982550000000003 ms max = 7.663642999999993 ms
终于出现了一个7ms的。因此我感觉靠使用纳秒为时间单位,并没有什么帮助。
-
Java不咋熟,不过看到10ms的误差,这个明显是操作系统调度的时间片,也就是系统那里调度线程的时间精度是10ms。 Win下有个Win32APItimeBeginPeriod,可以把时间片设置成1ms。这样误差应该能控制在1ms左右 再有,如果定时的回调逻辑能确保在触发间隔内完成,就是自己起个线程不停轮询时间。 最后,非实时操作系统,没有很精确的定时器,尤其是现在的抢占式多任务,线程的执行延迟是未知的。
-
定时器的精度跟操作系统跟硬件有关系。linux支持高精度纳秒级唤醒,windows只支持毫秒级唤醒,所以我猜测你用的是windows系统,你可以在linux上试试看,另外将线程的优先级定的高一些,以便唤醒后能更快得到CPU资源。
发表回复