Java 调度器延时存在误差,是否有更精确的调度方法?

毕设助手 论文问答 1

由于业务需要业务中需要非常精确的任务调度,例如100毫秒执行一次的业务,但是通过scheduledExecutor.scheduleAtFixedRate;执行的时候会经常出现10ms内的一个时间误差,例如有时候提前几毫秒,有时候推迟几毫秒,偶尔还会出现几十毫秒的差异,因为业务需求是希望差异尽可能的小,所以大家有什么好的办法能解决或者减小这种误差

回复

共3条回复 我来回复
  • 毕业设计驿站
    这个人很懒,什么都没有留下~
    评论

    首先,复习一把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的。因此我感觉靠使用纳秒为时间单位,并没有什么帮助。

    0条评论
  • 毕设客栈
    这个人很懒,什么都没有留下~
    评论

    Java不咋熟,不过看到10ms的误差,这个明显是操作系统调度的时间片,也就是系统那里调度线程的时间精度是10ms。 Win下有个Win32APItimeBeginPeriod,可以把时间片设置成1ms。这样误差应该能控制在1ms左右 再有,如果定时的回调逻辑能确保在触发间隔内完成,就是自己起个线程不停轮询时间。 最后,非实时操作系统,没有很精确的定时器,尤其是现在的抢占式多任务,线程的执行延迟是未知的。

    0条评论
  • 毕设港湾
    这个人很懒,什么都没有留下~
    评论

    定时器的精度跟操作系统跟硬件有关系。linux支持高精度纳秒级唤醒,windows只支持毫秒级唤醒,所以我猜测你用的是windows系统,你可以在linux上试试看,另外将线程的优先级定的高一些,以便唤醒后能更快得到CPU资源。

    0条评论

发表回复

登录后才能评论