AtomicInteger.incrementAndGet() 做限流会失效,因其缺乏时间维度约束,无法自动清零或滑动窗口,导致请求集中爆发或窗口撕裂;应改用时间分片(如秒级窗口)配合 ConcurrentHashMap 与 LongAdder 实现精准限流。
为什么直接用
做限流会失效
因为限流不是单纯计数,它需要「时间维度」约束。只靠
自增,数值永远涨,没法自动清零或滑动——你看到的“每秒最多100次”,实际可能前10毫秒就打满100次,后面990毫秒完全被拦住,体验断崖式下跌。
常见错误现象:
计数器一路飙升,但限流逻辑没生效;或者手动定时重置(比如用
每秒清零),结果在清零瞬间出现窗口撕裂:清零前最后一毫秒+清零后第一毫秒,两波请求叠加突破阈值。
别在业务线程里 sleep 等待窗口结束——这等于把非阻塞变成阻塞
别用
做窗口分界——毫秒级精度在高并发下容易误判同一窗口
避免用
或
,对象创建开销大,且不适用于高频 CAS 场景
用
+ 时间戳分片替代单个
比
更适合高并发累加,但它本身也不带时间语义。真正关键的是「把时间切片化」:比如按秒级窗口,就用当前秒数(
)做 key,每个窗口独立计数。
实操建议:
立即学习
“
Java免费学习笔记(深入)
”;
用
存储「窗口开始时间 → 当前计数」,key 是窗口起始秒的时间戳(如
)
每次请求先算当前窗口:
,再 get or compute if absent
调用
,然后立即检查
不主动清理旧窗口——靠
的弱引用或定期扫描
过期项(比如只保留最近3个窗口)
在限流里基本没用武之地
很多人想用
实现“仅当未超限时才更新”,但这是错的:CAS 只保证原子性,不保证逻辑正确性。两个线程同时读到 count=99,都判断“99 compareAndSet(99, 100),结果一个成功、一个失败——失败的那个线程已经做了业务判断,却还要兜底处理,反而增加复杂度。
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
更糟的是,如果阈值是动态的,或涉及多个变量(比如“剩余配额 + 时间戳”联合判断),
根本无法覆盖。
除非你在写底层无锁数据结构,否则对业务限流来说,
或
配合即时判断更直白可靠
和
行为差异在 JDK 9+ 已趋同,不用刻意区分,但都绕不开上面的逻辑缺陷
真要强一致性校验,请用
或分离读写路径,而不是硬套 CAS
滑动窗口必须自己算偏移,
不支持
固定窗口简单,但抖动大;滑动窗口平滑,可 Java 标准库没有
这种东西。所谓“滑动”,本质是维护一个数组或环形缓冲区,每个槽位存一个时间片的计数,然后每次请求要累加最近 N 个槽位的和。
这时候
单个变量完全不够用,必须用数组+索引控制:
定义
,比如 60 个槽代表 60 秒
用
定位当前槽
先
,再遍历所有槽 sum —— 但注意:遍历过程非原子,sum 可能不准,只能作为近似参考
更稳妥的做法是用
,每个槽独立累加,避免遍历时锁竞争
滑动窗口真正的复杂点不在计数,而在「如何让过期槽位的计数自然归零而不影响正在累加的请求」——这需要配合时间戳标记或延迟回收,不是靠某个函数就能一键解决的。
AtomicInteger.incrementAndGet()AtomicIntegerAtomicIntegerScheduledExecutorServiceSystem.currentTimeMillis()new Date()LocalDateTime.now()LongAdderAtomicIntegerLongAdderAtomicIntegerSystem.nanoTime() / 1_000_000_000ConcurrentHashMap1717023600Llong window = System.currentTimeMillis() / 1000adder.increment()adder.sum() ,超了就拒绝ConcurrentHashMapkeySet()compareAndSet()AtomicInteger.compareAndSet(expected, updated)compareAndSet()incrementAndGet()add()weakCompareAndSet()compareAndSet()StampedLockAtomicIntegerAtomicSlidingWindowAtomicIntegerAtomicInteger[] slots = new AtomicInteger[windowSize]int index = (int)(System.currentTimeMillis() / 1000 % windowSize)slots[index].incrementAndGet()LongAdder[]