system.currenttimemillis()在多线程下性能显著下降,因其依赖操作系统全局时钟源,需频繁内核态切换并引发锁竞争,实测100线程总耗时比单线程高10–100倍;优化宜用毫秒级atomiclong缓存、threadlocal缓存或nanotime测耗时。

System.currentTimeMillis() 在多线程环境下性能明显下降,不是因为它“慢”,而是因为其底层依赖操作系统全局时钟源,存在内核态切换开销和资源争用。高频并发调用时,耗时可能比单线程高数十甚至上百倍,尤其在 HPET 等低效时钟源系统上更显著。
为什么多线程下会变慢
核心原因在于系统级资源竞争:
- 每次调用需从用户态陷入内核态,执行类似
clock_gettime(CLOCK_REALTIME)或gettimeofday()系统调用 - Linux 内核中只有一个全局时间源(如 HPET、TSC),高并发读取会触发锁竞争
- 时钟更新频率有限(常见为 1–15ms),短时间内大量线程读到同一值,但争用仍发生
- JVM 无法绕过该机制,native 实现不可优化
典型性能对比数据
实测表明,相同调用次数下:
- 单线程循环调用 1000 万次:通常耗时 20–50ms
- 100 个线程各调用 10 万次(总 1000 万次):总耗时可达 300–2000ms,相差 10–100 倍
- 极端场景(如每毫秒数百次调用)下,CPU 占用率可升至 5% 以上
实用优化策略
不追求绝对精确时,可用轻量级缓存替代频繁系统调用:
-
毫秒级缓存时钟:用
ScheduledThreadPoolExecutor每 1ms 更新一次AtomicLong,业务线程直接读取——精度损失 ≤1ms,吞吐提升显著 -
线程本地缓存:配合
ThreadLocal<long></long>,每个线程缓存最近一次时间戳,适用于非严格排序场景 -
组合唯一标识:对需要唯一性(如 ID 生成)的场景,用
currentTimeMillis() + AtomicLong避免冲突,而非单纯提速 - 避免无谓调用:日志打点、监控埋点等若不要求毫秒级精度,可降频(如每 10ms 采样一次)或复用已有时间戳
替代方案选型建议
根据使用目标选择更合适的 API:
- 测耗时 → 优先用
System.nanoTime()(高精度、无系统调用、不受系统时间跳变影响) - 记录事件时间 → 若需持久化或跨系统对齐,仍用
System.currentTimeMillis(),但加缓存层 - 分布式唯一 ID → 用 Snowflake、TinyID 等算法,不依赖单一时间戳
- Java 9+ 可考虑
java.time.Clock.systemUTC().millis(),但底层仍是currentTimeMillis(),无实质性能提升











