TimeUnit是JDK并发包中以纳秒为基准、通过系数层级建模的枚举类,提供toXxx(向上转换)、convert(向下转换)等安全换算方法,并封装sleep/timedJoin等原语以统一处理单位转换与中断。

TimeUnit 是 JDK 并发包中一个轻量但设计精巧的枚举类,它不依赖外部状态,所有逻辑都封装在枚举常量自身的方法里。剖析它的源码,关键不是看“有多少方法”,而是理解其**单位建模方式、安全换算机制和 API 语义分层**。
单位建模:以“基准秒”为锚点的层级结构
TimeUnit 源码中每个枚举项(如 SECONDS、MINUTES)都实现了统一接口,但内部定义了各自相对于纳秒的换算系数(C0~C6)。例如:
- SECONDS 的 C3 = 1_000_000_000(即 1 秒 = 10⁹ 纳秒)
- MINUTES 的 C4 = C3 × 60(即 1 分钟 = 60 秒 = 60 × 10⁹ 纳秒)
- DAYS 的 C6 = C5 × 24(C5 是 HOURS 的系数)
所有 toXxx(long d) 方法本质都是 d × (当前单位系数 / 目标单位系数),并由 x(d, multiplier, ceiling) 统一处理——这个私有静态方法用 Math.multiplyExact 做溢出检查,避免整数乘法静默截断。
convert() 与 toXxx() 的底层分工
二者表面相似,但调用路径不同:
-
SECONDS.toMillis(5)→ 调用SECONDS.toMillis()→ 内部执行5 × 1000 -
MILLISECONDS.convert(5000, SECONDS)→ 实际调用SECONDS.toMillis(5000)的逆运算,即5000 / 1000,并用Math.divideExact保安全
也就是说:convert 是“别人给值,我转成自己单位”;toXxx 是“我按自己单位解释这个数,转成你想要的单位”。源码里 convert 方法体基本都是一行委托,语义清晰,不重复实现。
工具方法如何复用并发原语
像 sleep、timedJoin、timedWait 这些方法,并非重新实现线程控制,而是对 JDK 原生 API 的语义包装:
-
TimeUnit.SECONDS.sleep(3)→ 底层仍是Thread.sleep(3000),只是自动完成秒→毫秒转换 -
timedJoin(Thread t, 2)→ 调用t.join(2000)(单位为毫秒),并捕获InterruptedException后恢复中断标志 -
timedWait(Object obj, 10)→ 在 synchronized 块内调用obj.wait(10_000),同样做单位转换
这些方法没有新增 JVM 行为,而是把“单位换算 + 异常处理 + 中断传播”的模板逻辑收拢,减少开发者手写样板代码出错的概率。
为什么不用 double 或 BigDecimal?
源码全程使用 long,原因明确:
- 并发场景下,时间通常用于超时判断或调度,**精度要求是“整数量级”而非小数**(比如“3秒内响应”,不是“3.14159秒”)
- 纳秒级计时(
System.nanoTime())返回 long,直接对接无损耗 - 避免浮点误差导致的边界判断失败(如
0.1 + 0.2 != 0.3) - 所有换算都基于整数倍关系(60秒/分钟、24小时/天),无需小数表达
若业务真需亚毫秒精度,应单独用 System.nanoTime() 计算差值,再按需转 double —— TimeUnit 不越界承担该职责。


















