绝对超时指从网关接收请求到业务完成的端到端耗时不得超过硬性时限(如800ms),判定依据是service_finish_ms − gateway_recv_ms > MAX_TOTAL_MS,并预留buffer防误判,锁释放需校验时间戳防误删。

用 System.currentTimeMillis() 判定高频秒杀场景下的“绝对超时”,核心不是单纯比时间戳,而是要结合请求发起时刻、服务端接收时刻、锁持有时刻和业务处理耗时,构建一个**端到端可验证的超时边界**。它不能只看“锁有没有过期”,而要看“这次秒杀请求是否在全局允许的最大耗时内完成”。
明确“绝对超时”的真实含义
秒杀中说的“绝对超时”,是指从用户点击下单那一刻起,到最终返回成功/失败结果为止,整个链路**严格不能超过某个硬性时限(比如 800ms)**。这个时限由前端体验、库存一致性窗口、下游依赖(如 DB、缓存)的 SLA 共同决定,不是锁本身的 TTL。
而 currentTimeMillis() 只提供毫秒级系统时间,它本身不解决分布式时钟漂移、网络延迟、JVM 暂停等问题,所以必须配合合理的时间采集点和误差容忍设计。
关键时间点采集与校验逻辑
在入口处(如网关或 Controller)就记录请求到达时间,并透传给后续所有环节:
- client_start_ms:前端 JS 记录用户点击时间(需注意设备时钟不准,仅作参考)
-
gateway_recv_ms:网关调用
System.currentTimeMillis()获取请求接入时刻(最可靠起点) - service_acquire_ms:业务服务尝试获取分布式锁时记录的时间(如 Redis SETNX + PX 前)
-
service_finish_ms:业务执行完毕、准备返回响应前再次调用
currentTimeMillis()
真正用于判定“是否绝对超时”的是:
service_finish_ms − gateway_recv_ms > MAX_TOTAL_MS
而不是 service_finish_ms − service_acquire_ms > lock_ttl —— 后者只是锁粒度超时,不是业务端到端超时。
防误判:补偿网络与调度偏差
单纯用 currentTimeMillis() 在高并发下可能因以下原因误判超时:
- JVM Full GC 导致线程挂起几十甚至上百毫秒
- 容器 CPU 节流、线程调度延迟
- 跨机房调用引入不可控网络抖动
应对方式:
- 为
MAX_TOTAL_MS预留 buffer(例如设为 900ms,而非卡死 800ms) - 对
service_acquire_ms − gateway_recv_ms单独监控,若该延迟 > 200ms,说明网关或负载层已积压,直接快速失败,不进秒杀主流程 - 禁用基于时间戳的“动态续锁”逻辑(如 watch dog),避免掩盖真实超时问题
锁释放必须带时间戳校验(防止误删)
即使你用 currentTimeMillis() 判定了超时,也不能直接释放锁——因为可能其他线程刚拿到锁正要执行。安全做法是:
- 加锁时写入唯一 requestId + 当前时间戳(如
SET lock_key "req_abc_1715234567890" PX 500) - 释放锁前先 GET,检查 value 是否匹配且当前时间未超过其“预期有效截止时间”(即
lock_value_timestamp + ttl) - 用 Lua 脚本原子执行:GET → 判断时间 → DEL,避免竞态
这样,即使某次请求因超时被主动放弃,也不会错误清理掉另一个正常运行中的锁。

















