System.nanoTime() 的隐患

      前段时间项目中需要 统计接口连接时间,考虑到连接时间一般都是零点几毫秒级别的,为了拿到更精确地数值,没有使用System.currentTimeMillis(),而是贸然地使用System.nanoTime()来统计时间,后来分析服务器上的数据,发现 竟然有10-15%的数据数值竟然超过了 10的13次方。

     原因:

System.currentTimeMillis() 起始时间是基于 1970.1.1 0:00:00 这个确定的时间的,而System.nanoTime()是基于cpu核心的时钟周期来计时,它的开始时间是不确定的。(有篇文章说是更加cpu核心的启动时间开始计算的)

但是在多核处理器上,由于每个核心的开始时间不确定,但是在多核处理器上,

	long start = System.nanoTime();
		String ip = Utilities.getIpByUrl(url);
		long cost = System.nanoTime() - start;

 

 这段代码有可能会运行在两个不同的cpu核心上,从而导致得到的结果完全不符逻辑。

 

Returns the current timestamp of the most precise timer available on the local system, in nanoseconds. Equivalent to Linux's CLOCK_MONOTONIC.

This timestamp should only be used to measure a duration by comparing it against another timestamp from the same process on the same device. Values returned by this method do not have a defined correspondence to wall clock times; the zero value is typically whenever the device last booted. Use currentTimeMillis() if you want to know what time it is. 

     

package com.example; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.CompressionLevel; import net.lingala.zip4j.model.enums.CompressionMethod; import java.io.*; import java.nio.file.*; import java.util.*; public class ZipCompressionBenchmark { // 配置 private static final String OUTPUT_DIR = "/home/zhouweixiang/test/git/compress_test"; private static final String INPUT_FOLDER = "/home/zhouweixiang/test/git/59-git-download"; // 输入文件夹路径 private static final String PASSWORD = "123"; // ZIP 密码 public static void main(String[] args) { Path inputPath = Paths.get(INPUT_FOLDER); if (!Files.exists(inputPath) || !Files.isDirectory(inputPath)) { System.err.println("输入文件夹不存在或不是目录: " + inputPath.toAbsolutePath()); return; } Path outputDir = Paths.get(OUTPUT_DIR); try { Files.createDirectories(outputDir); } catch (IOException e) { e.printStackTrace(); return; } System.out.println("开始测试压缩算法性能..."); // 定义压缩方式和级别 Map<String, ZipParameters> compressionConfigs = new LinkedHashMap<>(); { ZipParameters storeParams = new ZipParameters(); storeParams.setCompressionMethod(CompressionMethod.STORE); storeParams.setEncryptFiles(false); compressionConfigs.put("STORE", storeParams); } { ZipParameters deflateFastestParams = new ZipParameters(); deflateFastestParams.setCompressionMethod(CompressionMethod.DEFLATE); deflateFastestParams.setCompressionLevel(CompressionLevel.FASTEST); deflateFastestParams.setEncryptFiles(false); compressionConfigs.put("DEFLATE-FASTEST", deflateFastestParams); } { ZipParameters deflateNormalParams = new ZipParameters(); deflateNormalParams.setCompressionMethod(CompressionMethod.DEFLATE); deflateNormalParams.setCompressionLevel(CompressionLevel.NORMAL); deflateNormalParams.setEncryptFiles(false); compressionConfigs.put("DEFLATE-NORMAL", deflateNormalParams); } // ZIP 压缩测试 for (Map.Entry<String, ZipParameters> entry : compressionConfigs.entrySet()) { String testName = entry.getKey(); ZipParameters params = entry.getValue(); Path zipFilePath = outputDir.resolve("output-" + testName + ".zip"); System.out.println("正在执行压缩测试: " + testName); long startTime = System.currentTimeMillis(); try { zipFolder(inputPath, zipFilePath, params, PASSWORD); } catch (Exception e) { System.err.println("压缩测试失败: " + testName); e.printStackTrace(); continue; } long duration = System.currentTimeMillis() - startTime; System.out.printf("压缩测试完成: %s,耗时 %.2f 秒%n", testName, duration / 1000.0); } System.out.println("所有压缩测试完成"); } /** * 使用 zip4j 将文件夹压缩为 ZIP 文件 */ private static void zipFolder(Path sourceFolderPath, Path zipFilePath, ZipParameters zipParameters, String password) throws Exception { try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), password != null ? password.toCharArray() : null)) { List<File> filesToAdd = new ArrayList<>(); if (Files.isDirectory(sourceFolderPath)) { Files.walk(sourceFolderPath) .filter(path -> !Files.isDirectory(path)) .map(Path::toFile) .forEach(filesToAdd::add); } else { filesToAdd.add(sourceFolderPath.toFile()); } for (File file : filesToAdd) { String zipEntryName = sourceFolderPath.relativize(file.toPath()).toString(); zipParameters.setFileNameInZip(zipEntryName); zipFile.addFile(file, zipParameters); } } } } 代码逻辑是否有问题
08-26
### 区别 - **时间精度**:`System.nanoTime()` 返回的是纳秒级别的时间计数,而 `System.currentTimeMillis()` 返回的是自1970年以来的毫秒数,前者精度更高 [^1]。 - **时间基准**:`System.nanoTime()` 基于的时间点是随机的,其返回值可能是任意数,甚至可能是负数;`System.currentTimeMillis()` 以1970年1月1日午夜(UTC)为基准,返回从该时间点到现在的毫秒数 [^1][^3]。 - **比较适用性**:`System.nanoTime()` 不适合与墙上时钟时间比较,仅当在 Java 虚拟机的同一实例中获得的两个此类值之间的差异被计算时才有意义;`System.currentTimeMillis()` 可用于获取当前系统时间,能直接与现实时间对应 [^1][^4]。 ### 使用场景 - **System.nanoTime()**:主要用于衡量一个时间段的长短,如一段代码执行所用的时间、获取数据库连接所用的时间、网络访问所用时间等 [^3]。 - **System.currentTimeMillis()**:适用于获取当前系统时间和进行基于时间的操作,例如在代码中需要记录事件发生的时间戳、进行定时任务等 [^1]。 ### 原理 - **System.nanoTime()**:基于系统的高精度计时器,该计时器可以提供纳秒级的时间精度,但它不与现实世界的时间相关联,其时间起点是系统启动时或者某个随机时间点 [^3]。 - **System.currentTimeMillis()**:通过操作系统的时钟服务,获取自1970年1月1日午夜(UTC)到当前时刻的毫秒数,这是一个与现实时间对应的时间戳 [^1]。 ### 代码示例 ```java public class TimeMeasurement { public static void main(String[] args) { // 使用 System.nanoTime() 测量代码执行时间 long startTime = System.nanoTime(); for (int i = 0; i < 1000000; i++) { // 模拟一些操作 } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println("代码执行时间(纳秒): " + duration); // 使用 System.currentTimeMillis() 获取当前时间 long currentTime = System.currentTimeMillis(); System.out.println("当前时间(毫秒): " + currentTime); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值