【问题标题】:Accurate timing in javajava中的精确计时
【发布时间】:2018-03-13 03:42:47
【问题描述】:

如何在 Java 中实现准确的计时?我知道 Thread.sleep() 不准确并导致 InterruptedExceptions。我正在尝试制作一个物理模拟器,并且我想要准确的代码。 到目前为止,这是我的代码:

public class FreeFall() {
    public static void main(String[] args) {
        double height = Double.parseDouble(args[0]);
        double xi = height;
        int counter = 0;
        while (true) {
            height = xi + 9.8 / * counter * counter;
            System.out.println(height);
            counter++;
            Thread.sleep(1000);
        }
    }
}

【问题讨论】:

  • 这是在 Java8+ 中吗?
  • 一个很好的起点是有关相关类的 Java8 教程:docs.oracle.com/javase/tutorial/datetime/TOC.html
  • 请发布编译的代码:)
  • 您的代码无法编译。 ... 9.8 / * ...
  • InterruptedExceptions 仅在其他线程中断睡眠线程时才会发生。

标签: java time


【解决方案1】:

tl;博士

Java 的传统实现不是实时系统。所以你不能指望确定性的性能

如果您坚持确定性时序,请寻求real-time Java 的实现。

类似问题:Java - alternative to thread.sleep

Thread.sleep

您可以要求以纳秒的分辨率进行睡眠,但由于当前计算机硬件的限制,您可能无法获得。请参阅the other sleep method 将第二个参数用于纳秒。

Thread.sleep( 0L , 300L ) ;  // Ask to sleep 300 nanoseconds.

Thread.sleep 的行为从来都不是确定性可靠的,如下所述。内部 CPU 调度(如乱序执行和超线程)、JVM 的 OS 调度、JVM 对线程的调度、偶尔的垃圾回收等诸多因素都会影响实际的执行时序。因此,除非您转向 real-time Java 实现,否则您永远不能指望可预测的执行。

System.nanoTime

要跟踪经过的时间,请致电System.nanoTime。这具有纳秒级的分辨率,但不太可能精确。截至 2018 年,传统的计算机硬件时钟只能精确到大约微秒,而不是纳秒。这适用于微基准测试(提示:JMH)。 System.nanoTime 与告知当前日期时间无关。返回的数字是来自一些未记录的原始时刻的计数(根据我的经验,自 JVM 启动以来,但不能保证)。 64 位的数字在连续运行几十年后可能会溢出。

long start = System.nanoTime() ; // Just some progressively incrementing number of nanoseconds. NOT the current date-time.
long stop = System.nanoTime() ;
long elapsed = ( stop - start ) ;  // Calculate elapsed time in a resolution as fine as nanoseconds (though not likely precise beyond microseconds).

Instant

要以nanoseconds 的分辨率获取UTC 的当前时刻,请使用java.time.Instant 类。在 Java 8 中,当前时刻只能捕获到毫秒,但在 Java 9 及更高版本中,Clock 中的fresh implementation 可能会提供更精细的分辨率。我看到microseconds 在 macOS Sierra 上的 Oracle JDK 9.0.4 中捕获。

Instant instant = Instant.now() ;  // Capture the current moment in UTC with a resolution of nanoseconds though likely with precision not as fine.

2018-03-16T01:18:54.898583Z

Duration

我建议通常按原样使用Instant 对象。您可以通过isBeforeisAfterequals 方法进行比较。要计算经过的时间,请使用Duration.between

Duration d = Duration.between( instantThen , Instant.now() ) ;  // A span of time not attached to the timeline, with a resolution of nanoseconds.

避免System.currentTimeMillis​()

至于System.currentTimeMillis​(),你可以忘记这个方法。你最好改用Instant

执行者

至于TimerTimerTask,您应该知道这些类现在是遗留的,被添加到Java 5 及更高版本的更复杂的Executor 框架所取代。见the Oracle Tutorial

实时 Java

即使使用 Executors,您也不能期望在执行过程中获得精确的瞬间准确性。主机操作系统控制 JVM 的调度,其性能可能会有所不同。在 JVM 中,线程是动态调度的,它们的性能可能会有所不同。特别是,垃圾收集可能会影响特定时刻的性能。

如果您想要确定的执行时间,您需要获得real-time Java 的实现。 Java 的传统实现,例如 Oracle JDK 和 OpenJDK,肯定是不是real-time systems


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

  • Java SE 8Java SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实现。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6Java SE 7
    • 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。
  • Android
    • java.time 类的 Android 捆绑包实现的更高版本。
    • 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    您可能想要利用时间戳,利用 System.currentTimeMillis(),这将为您提供自 1970 年 1 月 1 日以来经过的毫秒数。

    在您的物体开始下落时记录时间戳。然后,在您的循环中,您可以获取表示当前时间的时间戳。从currentTime 中减去startTime,您将得到准确的毫秒数。例如,您可以据此计算在给定恒定速度的情况下,您的对象在这段时间内应该下降多远。

    这种方法的好处在于,无论您是在快速还是慢速处理器上运行都无关紧要,因为您的时间戳反映了时间的实际流逝与循环的迭代次数。

    【讨论】:

    • 实际上,您可能会在 Java 9 及更高版本中获得更好的结果,方法是调用 Instant.now() 在微秒内获得结果,因为我在 macOS Sierra 上使用 Oracle Java 9.0.4。
    【解决方案3】:

    对于 Java 8 及更高版本 - 您可以使用 java.time 库 它有日期时间类

    日期 -

      //To create a new date
    LocalDate ld =  LocalDate.Of(2018,March,26);
       //To print current date
    LocalDate.now();
    

    为了时间-

     //To create a new time
    LocalTime lt = LocalTime.Of(11,22);
       //To print current time
    Local time.now();
    

    对于日期和时间 -

       //To create a new date time
    LocalDateTime ldt = LocalDateTime.Of(2018,March,26,11,22);
       //To print current date time
    LocalDateTime.now();
    

    注意:请注意这些事情- - 这些类仅在 Java 8 及更高版本中可用。 - 这些类有私有构造函数,所以你不能实例化它们 -Of 是一个静态方法,因此使用类名调用 - 这些类是不可变的,因此您需要创建的任何更新都将保存到新对象中

    【讨论】:

    • 这与问题有什么关系? LocalDateTime不是瞬间,也不代表时间线上的一个点。因此,与准确跟踪时间的问题无关。 LocalTime 同上。而LocalDate 是仅限日期的,对于问题中指示的时间跟踪来说,粒度太粗略了。
    • 更正我,LocalTime 以纳秒为单位给出时间。一个人可以减去两个时间得到经过的时间,精度高达纳秒
    • LocalTime 仅限于一个通用的 24 小时制时钟,没有日期或时区的概念。因此,该类不考虑夏令时 (DST) 等异常情况。您的结果将在 DST 转换时失败。您的结果将在午夜后失败。您要查找的类是 Instant,它是 UTC 时间线上的一个点,分辨率为纳秒,精度约为微秒,具体取决于主机计算机的硬件时钟。
    猜你喜欢
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    • 2017-07-22
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    相关资源
    最近更新 更多