Java调度执行器的准确性

Java调度执行器的准确性,java,scheduling,executor,Java,Scheduling,Executor,我在使用Java调度执行器时遇到了一个特殊情况,我想知道我所经历的是否正常 我需要安排以5秒预定义速率执行的任务。预计执行这些任务的时间有时会超过5秒,但当运行时间低于5秒时,备份的任务列表应快速连续运行以赶上进度。在运行任务时,了解原始计划的执行时间很重要(想想java.util.TimerTask中的scheduledExecutionTime())。最后,我需要跟踪计划时间和实际时间之间的差异,以确定计划何时“漂移”以及漂移的程度 到目前为止,我已经使用Java执行器实现了所有这些,下面的

我在使用Java调度执行器时遇到了一个特殊情况,我想知道我所经历的是否正常

我需要安排以5秒预定义速率执行的任务。预计执行这些任务的时间有时会超过5秒,但当运行时间低于5秒时,备份的任务列表应快速连续运行以赶上进度。在运行任务时,了解原始计划的执行时间很重要(想想
java.util.TimerTask
中的
scheduledExecutionTime()
)。最后,我需要跟踪计划时间和实际时间之间的差异,以确定计划何时“漂移”以及漂移的程度

到目前为止,我已经使用Java执行器实现了所有这些,下面的类说明了总体思路:

public class ExecutorTest {
    public static final long PERIOD = 5000;

    public static void main(String[] args) {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                new Command(), 0, PERIOD, TimeUnit.MILLISECONDS);
    }

    private static final class Command implements Runnable {
        long timestamp = 0;

        public void run() {
            long now = System.currentTimeMillis();

            if (timestamp == 0) {
                timestamp = now;
            }

            // Drift is the difference between scheduled time and execution time
            long drift = now - timestamp;

            String format = "Ran at %1$tF %<tT,%<tL; drift: %2$dms";
            System.out.println(String.format(format, now, drift));

            timestamp += PERIOD;
        }
    }
}
公共类测试{
公共静态最终长周期=5000;
公共静态void main(字符串[]args){
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
新命令(),0,句点,时间单位。毫秒);
}
私有静态final类命令实现Runnable{
长时间戳=0;
公开募捐{
long now=System.currentTimeMillis();
如果(时间戳==0){
时间戳=现在;
}
//漂移是计划时间和执行时间之间的差异
长漂移=现在-时间戳;

String format=“在%1$tF%运行时,计划执行器服务使用的System.nanoTime不会像currentTimeMillis那样漂移。除非您在具有多个CPU插槽的XP系统上运行。XP中存在一个错误,操作系统调用System.nanoTime()套接字之间的使用不一致,因此当线程切换它在哪个套接字上运行时,您可能会看到这种情况。(这在Vista/7上不是问题)

在具有一个套接字的Linux系统上,您的程序报告0-3毫秒漂移

试试这个程序

public static void main(String... args) throws Exception {
    long start = System.nanoTime();
    long time = start;
    while(time < start + 3e10) {
        long now = System.nanoTime();
        if (now < time || now > time + 50000) {
            System.out.println(now - time);
            now = System.nanoTime();
        }
        time = now;
    }
}
publicstaticvoidmain(String…args)引发异常{
长启动=System.nanoTime();
长时间=启动;
同时(时间<开始+3e10){
long now=System.nanoTime();
如果(现在<时间|现在>时间+50000){
System.out.println(现在-时间);
now=System.nanoTime();
}
时间=现在;
}
}

在i7系统上,我看到大约10次高达2毫秒的跳跃。如果我使用该机器,我会看到更多。我希望您可能会看到大量的负计时和正计时。

对我来说似乎很正常,它在5000/2的范围内变化。您应该尝试快速记录,而不是format+println,因此不会测量println的开销漂移。这会很有趣。