96SEO 2026-08-01 10:15 1
在一个带有流式输出、任务取消、排队限流、异步回调和分布式部署的后台程序里经常会遇到同一个问题:一个业务动作在逻辑上只能发生一次但在运行时可能被多个线程、多个回调、多个异常方法同时触发。
从典型场景包括来看,

这些问题的共同点不是业务流程复杂,而是终态竞争复杂。多个线程都认为自己应该收尾,但业务只允许一个线程真正完成收尾。按理说,
这类问题很适合使用AtomicBoolean或基于CAS的状态机来解决。它们不是为了替代分布式锁,而是在单个JVM内部。为某一个本地对象提供轻量、可靠、无锁的一次性控制。
AtomicBoolean.compareAndSet 可以理解成一个“一次性门闩”:
if ) {
return;}
// 只有第一个线程能执行到这里
doClose;老实说,
false。就把它改成true,并继续执行。true,表示有人做过了当前线程直接返回。它特别适合保护“只能做一次”的本地动作。例如关闭连接、取消请求、释放资源、写入终态、结束 Trace。
关键提示: 此CAS仅在当前进程内生效,无法跨机器互斥。如果程序是分布式部署,通常需要两层协作: 1️⃣ 分布式层负责把信号送到正确节点;2️⃣ 本地层负责让该节点只执行一次。
从最常见写法来看。
private final AtomicBoolean cancelled = new AtomicBoolean;public void cancel {
if ) {
return;}
cancelRemoteCall;persistPartialResult;notifyClient;releaseResource;}
⚠️ 痛点:使用者多次点击“停止”,网络重试导致多条取消请求;如果没有CAS,每条请求都会尝试关闭连接或保存结果。造成资源浪费和数据混乱,
private final AtomicBoolean closed = new AtomicBoolean;public void send {
if ) {
return;}
try {
doSend;} catch {
fail;}
}
public void complete {
if ) {
doComplete;}
}
public void fail {
if ) {
doCompleteWithError;}
}
✅ 好处:避免重复关闭连接;保证完结/错误/取消三种终态只能出现一次;减少无意义的数据传输与错误日志。⏱️ 同时通过.get 快速检查是否已关闭,但仍需在发送异常时统一走fail以消除窗口竞态。⏳
Cancelling a long-running HTTP call typically needs two actions:
private final AtomicBoolean once = new AtomicBoolean;其实,private final AtomicBoolean cancelled = new AtomicBoolean;public void cancel {
if ) return;cancelled.set;call.cancel,}
"即使上层因超时/使用者取消/兜底等原因重复调用底层cancel,底层仍保持幂等。"✋🏽
RUNNING 转到唯一最终状态
类似思路还可用于首包只记录一次:
java
if){
recordFirstTokenLatency;}
但这关注的是“首次事件”,而非终态事件。🕒
`` 更清晰表达多状态转移`当业务状态不止两个时单纯用 Boolean 就无法表达。例如排队限流中的请求可能有多个终态:
PENDING -> GRANTED
PENDING -> TIMED_OUT
PENDING -> CANCELLED
此时更适合用 AtomicReference
java enum State { PENDING。GRANTED,TIMED_OUT,CANCELLED }
private final AtomicReference
void timeout { if (!state.compareAndSet(State.PENDING,State.TIMED_OUT)) return;cleanup,onTimeout;}
boolean grant { if (!state.compareAndSet(State.PENDING,State.GRANTED)) return false;runBusiness,return true;}
至于主要思想,所有终态都从同一 PENDING 状态抢占;谁先 CAS 成功谁就是最终状态,其余方法放弃业务回调,只做必要清理。
再看常见用途。
相比 AtomicBooleanAtomicReference 的好处是状态表达更清晰,更易追踪日志与推断并发行为。老实说,
模式六: 通知合并避免重复扫描`在分布式限流或队列程序中。一个释放资源动作可能触发大量通知。如果每个通知都立刻启动一次扫描,就容易形成通知风暴。
可以用一个本地 CAS 门闩表示“当前是否已有扫描任务在跑”:
java private final AtomicBoolean firing = new AtomicBoolean;private final AtomicInteger pendingNotifications = new AtomicInteger;
void fire{ pendingNotifications.incrementAndGet;if){ return,怎么说呢,} executor.execute ->{ try{ drainNotificationsAndPoll;}finally{ firing.set;} }),}
至于业务意义,
这不是为了保证“只执行一次”,而是把“高频重复通知”合并成“尽量少的有效扫描”。
模式七: 本地心跳句柄的关闭与丢失`可续期任务或调度锁通常有后台心跳对象。也会面对多条线程:
| 场景 | 操作 |
|---|---|
| 心跳发现续期失败 | 标记锁已丢失 |
| 应用完成 | 主动关闭心跳 |
| 应用关闭 | 停止调度 |
说到两种本地状态,java
lost : 锁是否已确认丢失;closed : 心跳是否已主动关闭;
都适合用 AtomicBoolean
java
void markLost{
if){
cancelFuture;}
}
void close{
if){
cancelFuture;}
}
区别两种 CAS:
1️⃣ 数据库层 CAS 用于跨实例争抢/续期分布式锁。2️⃣ JVM 层 CAS 用于管理本机心跳对象生命周期。
前者负责跨实例互斥;后者防止同一对象被多次关闭或定时任务被重复撤销。话说回来,
不是所有AtomicBoolean 都是 CAS 门闩`
项目中还会看到一种 AtomicBoolean 用法:在 Map.compute 或 lambda 内部作为可变返回容器。例如的观点是,
java
AtomicBoolean allowed = new AtomicBoolean;map.compute->{
if){ allowed.set;}
return nextValue;}),return allowed.get;
这种写法主要不是 CAS,而是 Java lambda 对外部局部变量不可变限制——通过盒子把结果带出来。如果没有调用 compareAndSet不要将其视为“一次性门闩”。老实说,
| 为什么选择CAS+AtomicBoolean?& 常见痛点在哪里, |
|---|
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback