System.currentTimeMillis() 和sleep 存在误差原因与解决方案

本文探讨了Java中System.currentTimeMillis()与sleep()方法存在的精度误差,并介绍了如何利用System.nanoTime()来提高计时精度,减少误差。同时提供了一个通过动态调整sleep时间来确保任务执行总数准确性的算法实例。

System.currentTimeMillis() 与sleep()存在误差。

精度跟系统相关,10ms-15ms为常见误差,多媒体操作多用  System.nanoTime()  返回纳秒值。

即用  System.nanoTime()  /1000000L 替换  System.currentTimeMillis() 



参考jdk文档:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。


当然计时器应该积极使用timer,因为它所使用的是wait()而非sleep(),但是仍然存在时钟摇摆,线程调度的误差 。

假如只对调用总数要求高,对时间间隔允许误差的话可以加入如下算法保证调用总数精准,动态补齐 sleep的时间。


例如:

假如想要一个时钟每隔33.3333毫秒执行,执行间隔可以不精准,但执行总数一定要是1秒30次;

private class VideoRecordThread extends Thread {
        final private int spaceArray[]= {32,33,35};
        private int spaceindex = 0;
        private long lastspaceTime = 0;
        private boolean mState  = true;
        @Override
        public void run() {
            while (mState) {

                    if(spaceindex >2)
                        spaceindex = 0;
                    mReadTime = spaceArray[spaceindex++];
                    /*
                       *do something
                       */
                    long deviation =  mLastTime - mNowTime -lastspaceTime;
                    mNowTime = System.nanoTime()/1000000L;
                    long spaceTime = mReadTime - (mNowTime - mLastTime) - deviation;
                    if(spaceTime > 2*mReadTime || spaceTime<0)
                        spaceTime = mReadTime;

                    try {
                        sleep(spaceTime);
         
                        lastspaceTime = spaceTime;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    mLastTime = System.nanoTime()/1000000L;
                
            }
      
        }
         public void stop();
           {
            
                mState = false;
            }
    }


<think>好的,我现在需要解释JavaSystem.nanoTime()System.currentTimeMillis()的区别及适用场景。首先,我得回忆一下这两个方法的基本用途特点。 System.currentTimeMillis()应该是返回当前时间的毫秒数,从1970年1月1日UTC开始的,也就是通常的时间戳。这个方法可能受到系统时间调整的影响,比如用户修改了系统时间,或者有闰秒之类的,所以它可能不适合用来测量时间间隔,特别是短时间的测量。 然后是System.nanoTime(),这个方法提供的是纳秒级别的计时,但它不是基于实际的时间戳,而是基于某个不确定的起点,比如JVM启动的时间。所以它更适合用来测量时间间隔,尤其是高精度的需求,比如性能测试或计算代码执行时间。不过,不同平台上的实现可能不同,跨平台的一致性可能有问题,这点需要注意。 接下来,我需要确认它们的主要区别。首先是精度,currentTimeMillis是毫秒,而nanoTime是纳秒,显然更高。然后是用途,currentTimeMillis适合获取当前时间戳,而nanoTime适合测量经过时间。另外,起点不同,currentTimeMillis是固定的UTC时间点,而nanoTime是随机的起点,可能在负数。还有是否受系统时间影响,currentTimeMillis会受系统时间调整的影响,而nanoTime不会,因为它只计算经过的时间。 适用场景的话,currentTimeMillis适合需要时间戳的情况,比如记录事件发生的时间,或者日期计算。而nanoTime适合需要高精度时间测量的情况,比如性能分析、短时间间隔的计算,比如计算一段代码的执行时间。 还需要注意跨平台的问题,因为nanoTime的实现可能依赖底层操作系统,不同JVM可能有不同的行为。例如,有的系统可能不支持纳秒级的精度,或者在不同操作系统上返回值的稳定性不同。这点在引用中有提到,比如引用3提到nanoTime是平台相关的,可能存在不同JVM实现的差异。 另外,关于时间戳的长度,currentTimeMillis返回的是13位(毫秒),而nanoTime返回的是纳秒数,可能更长,但通常只用来计算差值,而不是绝对时间。比如,引用4中提到,在需要非常短时间间隔的计算时应该使用nanoTime,而日期计算用currentTimeMillis。 可能还需要提到线程安全的问题,不过这两个方法应该都是线程安全的,因为它们只是读取系统状态,不涉及共享变量的修改。 现在需要组织这些点,结构要清晰。可能需要分点列出区别,然后适用场景。同时,引用相关的引用资料来支持观点,比如引用4中的内容。 还有用户的问题是想了解区别及适用场景,所以回答需要覆盖这两个方面。可能需要先介绍每个方法,然后对比它们的差异,最后给出各自的适用情况。同时,生成相关问题,比如如何选择这两个方法,或者它们在不同场景下的限制等。 最后,要确保使用正确的中文表达,避免技术术语错误,同时符合LaTeX格式要求,但这里的问题可能不需要数学公式,不过如果有变量或代码,需要正确格式化。例如,方法名可能需要用代码格式,比如`System.nanoTime()`。 总结一下,回答的结构应该是: 1. 两个方法的简介 2. 主要区别(精度、用途、起点、是否受系统时间影响、跨平台性) 3. 适用场景(各自适合的情况) 4. 注意事项(如nanoTime可能返回负数,currentTimeMillis的闰秒问题等) 5. 相关问题 需要检查引用是否正确应用,例如引用4提到选择方法的依据,引用3提到跨平台问题,引用21补充更多使用细节。</think>### JavaSystem.nanoTime()System.currentTimeMillis()的区别及适用场景 #### 1. 方法简介 - **`System.currentTimeMillis()`** 返回从**1970年1月1日UTC**至今的毫秒数(13位时间戳),常用于获取当前系统时间或日期计算[^4]。 - **`System.nanoTime()`** 返回基于**JVM启动或特定平台事件**的纳秒级计时值,适合测量时间间隔,但起点不确定且可能为负数[^3]。 #### 2. 核心区别 | **特性** | **System.currentTimeMillis()** | **System.nanoTime()** | |-----------------------|------------------------------------------|--------------------------------------| | **精度** | 毫秒级(1ms) | 纳秒级(1ns,实际精度依赖平台) | | **用途** | 获取时间戳、日期计算 | 测量代码执行时间、短时间间隔计算 | | **时间起点** | 固定(1970-01-01 UTC) | 不确定(通常为JVM启动时) | | **受系统时间影响** | 是(如系统时间修改会影响结果) | 否(仅测量经过时间) | | **跨平台一致性** | 是 | 否(不同JVM实现可能不同) | | **返回值范围** | 始终为正数 | 可能为负数(需用差值计算) | #### 3. 适用场景 - **优先使用`currentTimeMillis()`的情况** - 记录事件发生的时间戳(如日志时间)[^2]。 - 需要日历日期转换的场景(如计算两天间隔)。 ```java long timestamp = System.currentTimeMillis(); // 输出:1659984000000(13位) ``` - **优先使用`nanoTime()`的情况** - 测量代码段执行时间(尤其是微秒/纳秒级需求)[^4]。 - 性能测试或高精度计时(如游戏循环、物理模拟)。 ```java long start = System.nanoTime(); // 执行代码 long duration = System.nanoTime() - start; // 单位:纳秒 ``` #### 4. 注意事项 - **`nanoTime()`的跨平台问题** 不同操作系统或JVM可能对纳秒精度的支持不一致,需测试验证。 - **`currentTimeMillis()`的闰秒问题** 系统时间可能因闰秒调整导致时间戳“回退”。 - **避免混合使用** 两者时间起点不同,差值计算无意义。 #### 5. 示例对比 | **场景** | **currentTimeMillis()** | **nanoTime()** | |------------------------|-------------------------|----------------------| | 获取当前时间 | ✔️ | ❌ | | 计算代码执行1ms内的耗时| ❌(精度不足) | ✔️ | | 跨时区的日期转换 | ✔️ | ❌ |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值