应在循环外缓存 System.currentTimeMillis() 并复用,避免高频调用引发内核态切换、手动格式化导致计算开销及误作唯一标识引发冲突;测时用 nanoTime(),生成 ID 用 UUID 或 AtomicLong。

直接在循环里组装 System.currentTimeMillis() 不仅逻辑错误,还会引发性能剧烈退化——它不是字符串拼接工具,也不是时间构造器。真正的问题不在“用了”,而在“怎么用”:高频调用触发内核态切换、手动拆解引入大量计算、误当唯一标识导致冲突,三者叠加会让吞吐骤降、延迟飙升。
别在循环里做格式化或拆解
常见错误是把毫秒值当成年月日时分秒的原料,在循环中反复计算:
- 手算年份/月份/闰年/时区偏移——JDK 的
java.time已封装全部规则,自己实现极易出错且无必要 - 每次循环都 new
SimpleDateFormat或重复调用format()——线程不安全、对象创建开销大 - 正确做法:用
static final DateTimeFormatter配合Instant.ofEpochMilli(millis),一次解析、复用到底
避免高频调用本身
currentTimeMillis() 是 native 方法,每次调用都涉及用户态到内核态切换,在高并发下争抢全局时钟源,性能呈非线性下降:
- 单线程调用百万次可能耗时几百毫秒;100 线程并发调用,总耗时可能翻倍甚至更高
- 解决方案:用
volatile long cachedTime缓存,后台线程每 1–5ms 更新一次,业务代码只读不调用 - 更优场景:Web 请求统一在拦截器获取一次时间戳,注入上下文供后续模块复用
不用它做计时或生成 ID
这两个用途与 currentTimeMillis() 的设计初衷完全错配:
- 测耗时请用
System.nanoTime()——精度高、不受系统时钟调整影响,适合超时控制、压测、倒计时 - 生成唯一标识不要拼
currentTimeMillis() + counter——毫秒级分辨率在高并发循环中必然重复,应改用UUID.randomUUID()或AtomicLong加随机后缀 - 串口防抖、高频数据过滤等场景,必须换
nanoTime(),否则用户手动调时间会导致负差值、逻辑失效
区分用途,选对 API
不是所有“要时间”的地方都该用 currentTimeMillis():
- 记录日志、审计、调度 → 用
Instant.now()(底层仍是currentTimeMillis(),但语义清晰、可读性强) - 测量间隔、判断超时、环形缓冲采样 → 必须用
nanoTime() - 需要缓存+低精度打点(如监控采样)→ 自建
CachedClock,避免每次 JNI 调用



















