Java高性能限流组件应基于AtomicInteger、LongAdder、StampedLock或ConcurrentHashMap,结合滑动窗口、令牌桶等算法,避免重量级同步;LongAdder适用于固定窗口计数,StampedLock优化滑动窗口读写,AtomicBoolean与ScheduledThreadPoolExecutor实现惰性令牌桶,需规避锁阻塞、远程调用及ThreadLocal滥用。

Java 中实现高性能限流组件,核心是合理选用并发工具类来避免锁竞争、保证线程安全,同时兼顾低延迟与高吞吐。推荐以 AtomicInteger、LongAdder、StampedLock 或 ConcurrentHashMap 为基础,配合滑动窗口、令牌桶等算法,而非直接依赖重量级同步或阻塞队列。
用 LongAdder 实现高并发计数型限流(固定窗口)
固定窗口限流最轻量,适合 QPS 级粗粒度控制。LongAdder 比 AtomicInteger 在高并发下性能更好,因为它通过分段累加减少 CAS 冲突。
- 为每个时间窗口(如每秒)维护一个
LongAdder计数器,键为时间戳(如System.currentTimeMillis() / 1000) - 使用
ConcurrentHashMap<Long, LongAdder>存储窗口计数,自动处理并发写入 - 请求到来时:获取当前窗口 key → 调用
add(1)→ 检查是否超限(如 >= 阈值)→ 超限则拒绝 - 注意清理过期窗口(可异步扫描 + remove,或用
computeIfAbsent+ 定期 compact)
用 StampedLock 实现低开销滑动窗口限流
滑动窗口比固定窗口更平滑,但需维护时间片内请求数。用 StampedLock 替代 synchronized 或 ReentrantLock,能显著降低读多写少场景下的争用开销。
- 维护一个环形数组(如长度为 10,代表最近 10 个 100ms 时间片),每个槽存该时间段请求数
- 读操作(判断是否限流)用
tryOptimisticRead + validate,几乎无锁;失败再降级为悲观读锁 - 写操作(更新当前时间片)仅在窗口滚动或计数时获取写锁,持续时间极短
- 窗口推进逻辑由请求触发(懒更新),避免定时任务调度开销
用 AtomicBoolean + ScheduledThreadPoolExecutor 实现令牌桶(轻量版)
标准令牌桶常依赖 ScheduledExecutorService 定期补充令牌,但频繁调度有开销。可用“惰性补发 + 原子状态”优化:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
- 维护
AtomicLong tokens和AtomicLong lastRefillTime - 每次请求前,先读取当前时间,计算应补充令牌数:
rate * (now - lastRefillTime) / 1e9 - 用
compareAndSet尝试更新lastRefillTime和tokens(CAS 循环确保原子性) - 若成功,则消耗令牌;否则重试或拒绝 —— 全程无锁,仅靠原子变量
- 避免使用
ScheduledFuture定时填充,消除调度器瓶颈和内存泄漏风险
规避常见性能陷阱
高性能限流不是堆砌并发工具,而是克制使用:
- 不用
ReentrantLock.lockInterruptibly()或wait/notify做限流等待 —— 阻塞会拖垮吞吐 - 避免在限流判断中调用远程服务、数据库或复杂计算 —— 所有逻辑必须在微秒级完成
- 慎用
ThreadLocal缓存窗口状态 —— 可能导致内存泄漏,且无法跨线程共享,不适合分布式上下文 - 阈值配置建议外部化(如 Apollo/Nacos),运行时热更新,避免重启应用
不复杂但容易忽略:限流效果最终取决于时钟精度、JVM 停顿、以及是否与业务链路耦合太紧。把限流逻辑下沉到网关或 RPC 框架拦截器层,通常比在业务 service 层做更高效、更统一。


















