system.currenttimemillis() 仅适用于粗粒度、单机、非关键路径的时间打点,因其反映墙钟时间而非 cpu 执行时间;适用场景包括 http 响应预估、批处理起止标记、超时控制及用户感知环节;打点须精准落在业务执行边界,避免框架拦截混入无关开销。

System.currentTimeMillis() 不适合精确测量业务逻辑耗时,但可用于粗粒度、单机、非关键路径的时间打点——重点是用对位置、留好原始时间戳、避开跨节点误用。
适用场景要拎清
它反映的是“墙钟时间”,不是 CPU 执行时间。所以只适合:
- HTTP 接口整体响应预估(比如日志里记个“耗时 842ms”供人工排查)
- 批处理任务起止标记(如“凌晨2:15开始,2:23结束”)
- 超时控制(如等待第三方响应,5秒未返回就中断)
- 与用户感知强相关的环节,比如页面加载完成、文件上传回调触发点
打点位置决定监控价值
别在框架入口/出口统一拦截,那会混入无关开销。真正要测的,是业务代码实际执行边界:
- Controller 层:参数校验通过后、业务方法调用前打起点;return 语句前打终点
- DB 操作:PreparedStatement.execute() 前后,而不是 MyBatis 的 sqlSession.select() 外层
- RPC 调用:Netty writeAndFlush() 发出前、收到 response 并反序列化完成后打终点
- 异步任务:线程池 beforeExecute / afterExecute 回调里记录
写法上几个硬性注意点
裸写 start/end 容易翻车,关键细节不能漏:
- 变量必须用 long 类型,避免 int 溢出(尤其跨天或长任务)
- 务必用 end - start,不是 start - end
- 异常路径必须覆盖:用 try-finally 或 try-with-resources 封装,确保终点一定被记录
- 日志中同时输出 startTs(毫秒时间戳)和 costMs,便于后续发现时钟回拨(比如 costMs 为负数)
- 别在高频循环里反复调用,单次调用开销虽小,但百万次叠加可能成瓶颈
生产环境别手写计时逻辑
手动插桩维护成本高、易遗漏、难对齐分布式链路。推荐分层处理:
- Web 层:用 Spring AOP 或 Filter,底层用 System.nanoTime() 测间隔
- RPC 层:Dubbo Interceptor 或 OpenFeign 的 ClientInterceptor
- 异步任务:包装 ExecutorService,重写 beforeExecute / afterExecute
- 全链路追踪:集成 SkyWalking 或 OpenTelemetry,自动关联各段 nanoTime 差值与 traceId











