Java中用循环数组实现固定长度时间窗口统计,核心是环形缓冲区存储时间槽计数,结合时间戳判断数据有效性;支持惰性清理与原子更新,适用于QPS统计等高频低延迟场景。

在 Java 中用数组实现固定长度时间窗口统计,核心思路是:用循环数组(环形缓冲区)存储每个时间槽的计数,配合时间戳判断数据是否落在当前窗口内。这种方式轻量、无依赖、适合高频低延迟场景,比如 QPS 统计、错误率采样等。
设计一个带时间槽的循环数组
假设窗口长度为 60 秒,精度为 1 秒 → 需要长度为 60 的 long[] 数组;若精度为 100ms、窗口 10 秒,则需 100 个槽。关键点:
- 每个数组下标对应一个时间槽,例如 index = (timestamp / slotDuration) % array.length
- 用一个 volatile long 记录当前“基准时间起点”,或每次直接用 System.currentTimeMillis() / slotDuration 计算逻辑时间戳
- 写入前先清理过期槽位:遍历所有槽,若其代表的时间范围已完全落在窗口外,就清零(也可惰性清理,见下文)
支持自动过期与原子更新
避免每次写都遍历全部槽,可采用“惰性清理 + 当前窗口边界校验”策略:
- 维护两个变量:currentWindowStart(毫秒级,当前窗口起始时间,对齐到 slot 边界)和 lastCleanupTime
- 每次 add() 时,先检查 timestamp 是否在 [currentWindowStart, currentWindowStart + windowSize) 内;不在则跳过或触发窗口滑动
- 滑动时重新计算 currentWindowStart,并只清空那些 所属逻辑时间戳 的槽位(最多清一次,非全扫)
- 使用 AtomicLongArray 或 synchronized 块保障单槽写入线程安全(通常单槽只会被同一逻辑时间戳的请求命中,冲突低)
提供常用统计接口(如 sum、avg、max)
数组本身不保存原始数据,只存聚合值(如请求数、耗时总和、失败次数),因此统计方法直接遍历有效槽位即可:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- getTotal():累加所有未过期槽的值(注意:需再次校验每个槽对应的时间是否仍在窗口内)
- getQps():getTotal() / windowSeconds(浮点结果,或返回 long 表示平均每秒整数次)
- getMaxInSlot():遍历有效槽,取最大值(适合监控峰值)
- 若需分位数(如 P95),此方案不适用——数组只存聚合值,无法还原原始分布,需换用 TDigest 或 HdrHistogram
一个极简可运行示例(60 秒窗口,1 秒粒度)
(生产中建议封装为类并加单元测试)
public class TimeWindowCounter {
private final long windowSeconds = 60;
private final long slotMillis = 1_000;
private final int capacity = (int) (windowSeconds * 1000 / slotMillis); // 60
private final long[] slots = new long[capacity];
private final Object lock = new Object();
<pre class="brush:php;toolbar:false;">public void add(long timestamp, long value) {
int idx = (int) ((timestamp / slotMillis) % capacity);
long windowStart = (timestamp / slotMillis) * slotMillis - (timestamp / slotMillis) % capacity * slotMillis;
// 实际中建议用更鲁棒的窗口对齐方式,此处仅为示意
synchronized (lock) {
// 清理过期槽:仅清理明显超出窗口的(保守做法)
long cutoff = timestamp - windowSeconds * 1000;
for (int i = 0; i = cutoff) sum += slots[i];
}
}
return sum;
}}
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










