system.currenttimemillis() 仅用于获取当前时间毫秒值,不能直接限频;需结合滑动窗口、固定窗口或令牌桶等算法实现限频,如固定窗口法通过比较当前秒级窗口与记录窗口来重置计数并判断是否超限。

System.currentTimeMillis() 本身只是一个获取当前时间毫秒值的静态方法,它不提供限频能力,也不能直接实现限频访问控制。要实现限频(如每秒最多调用 N 次),需要结合该时间戳做逻辑判断,常见做法是用滑动窗口、固定窗口或令牌桶等算法,而 currentTimeMillis() 主要用于获取时间基准。
基于固定时间窗口的简单限频
以“每秒最多 5 次”为例:记录最近一次窗口起始时间(比如取整到秒)和该窗口内调用次数。每次请求时,先检查是否进入新窗口——若当前时间所属的秒与记录的窗口秒不同,则重置计数器并更新窗口时间;再判断计数是否超限。
示例逻辑:
- 用两个变量:
long windowStart(当前窗口起始毫秒,如System.currentTimeMillis() / 1000 * 1000)、int count - 每次请求计算
long now = System.currentTimeMillis(),得到当前秒级窗口long currentWindow = now / 1000 * 1000 - 若
currentWindow != windowStart,则count = 0; windowStart = currentWindow; - 若
count ,允许访问并 <code>count++;否则拒绝
滑动窗口(更精确,需少量内存)
固定窗口有边界突变问题(如窗口切换瞬间可能多放行)。滑动窗口可记录最近一段时间(如最近 1000ms)内每次访问的时间戳,每次请求前清理过期时间戳(timestamp ),再判断剩余数量是否 ≤ 阈值。
可用 ConcurrentLinkedQueue<long></long> 或 ArrayDeque(注意线程安全):
- 插入前先遍历队列,移除所有
的时间戳 - 若队列 size now 并放行;否则拒绝
- 注意:高频场景下频繁遍历影响性能,可改用环形数组 + 时间分片优化
配合原子类或分布式方案的注意事项
单机场景下,可用 AtomicLong 记录时间戳与计数,但要注意避免 ABA 问题;更稳妥的是用 synchronized 或 ReentrantLock 保护共享状态。
若服务集群部署,各节点本地限频会失效,此时 currentTimeMillis() 仍可用作时间基准,但计数需外存(如 Redis):
- 用 Redis 的
INCR+EXPIRE实现固定窗口(key 命名为limit:20240520:123456:1s) - 用 Redis + Lua 脚本实现滑动窗口(如
ZADD+ZREMRANGEBYSCORE+ZCARD) - 无论哪种,都依赖
System.currentTimeMillis()生成客户端时间参数或校验服务端时间一致性
为什么不直接用 System.nanoTime()?
System.nanoTime() 更适合测量间隔(精度高、无系统时间跳变干扰),但它的值无绝对时间意义,不能跨 JVM 或持久化使用。限频通常需与真实时间对齐(如“每分钟”“每天”),所以仍要依赖 currentTimeMillis() 或 Instant.now()(推荐 Java 8+)作为时间源。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











