System.currentTimeMillis可作为基础工具实现固定窗口、滑动窗口和令牌桶限流,但仅适用于单机场景;需配合ConcurrentHashMap、时间戳清理或令牌推算逻辑,注意时钟跳变、窗口突刺及多实例状态不共享问题。

System.currentTimeMillis 本身只是获取当前毫秒时间戳,不直接实现限流,但可以作为基础工具配合简单逻辑来实现“固定窗口”或“滑动窗口”类的限流。关键在于用它记录时间边界、判断请求是否在允许范围内。
固定窗口限流(最简实现)
把时间划分为固定长度的窗口(比如每秒一个窗口),统计该窗口内请求数,超限就拒绝。
- 用 currentMillis / 1000 计算当前秒级窗口标识(如 1717023456 表示第 1717023456 秒)
- 用 ConcurrentHashMap 存储 窗口标识 → 请求计数,避免并发问题
- 每次请求时,先获取当前窗口 key,再原子递增计数;若计数 > 阈值(如 100),则拒绝
- 注意:窗口切换瞬间可能出现“突刺”,比如前一秒末 + 后一秒初各接近阈值,合起来超限
滑动窗口简易版(用时间戳差判断)
不依赖外部存储,只保留最近 N 次请求的时间戳,适合低频或单机轻量场景。
- 用 ArrayList 或 LinkedList 缓存最近请求的 System.currentTimeMillis() 值
- 每次新请求到来,先清理队列中 早于 now - windowMs 的旧时间戳(如 windowMs=1000 表示 1 秒窗口)
- 检查剩余数量是否 >= 阈值,是则限流;否则添加当前时间戳并放行
- 缺点:高并发下频繁遍历和删除影响性能,建议搭配环形数组或更高效结构
令牌桶模拟(无定时器的简化版)
用时间戳推算“应有令牌数”,避免启动定时任务,适合对精度要求不高的场景。
立即学习“Java免费学习笔记(深入)”;
- 维护 lastRefillTime 和 tokenCount,每次请求前计算 (now - lastRefillTime) / refillIntervalMs 得到新增令牌数
- 更新 tokenCount = min(maxToken, tokenCount + 新增数),然后 lastRefillTime = now
- 若 tokenCount > 0,则 tokenCount-- 并放行;否则拒绝
- 注意:浮点运算可能引入误差,建议用整数运算(如按毫秒累计,只在整除 refillIntervalMs 时补发)
实际使用注意事项
纯靠 System.currentTimeMillis 实现的限流,适用于单机、低 QPS 或原型验证。生产环境需考虑:
- 多实例部署时,固定窗口/滑动窗口无法共享状态,需接入 Redis 或分布式协调服务
- System.currentTimeMillis 可能因系统时钟调整(NTP)发生跳变,导致窗口错乱;高可靠场景建议用 System.nanoTime() 做相对时间测量
- 限流结果要明确返回(如抛异常、返回特定码),避免静默失败


















