atomicboolean.compareandset() 能保证单次执行,因其底层基于cpu级cas指令,原子性完成“判断并更新”,值未被修改才写入新值,否则返回false,精准实现“只做一次”语义。

为什么 AtomicBoolean.compareAndSet() 能保证单次执行
因为它的底层是 CPU 级的 CAS 指令,不是“加锁后判断再改”,而是“原子地判断并更新”——值没被别人动过,才写入新值;一旦中间有别的线程抢先改了,这次操作就失败,返回 false。这正好对应“只做一次”的语义:成功即执行,失败就跳过。
常见错误是把它当普通布尔赋值用:flag.set(true) 或 if (flag.get()) { ... },这样无法防止多个线程同时进入临界逻辑。
- 必须用
compareAndSet(false, true)作为“准入门禁”,而不是先get()再判断 - 返回
true才代表当前线程抢到了唯一执行权;false就该直接退出或降级处理 - 注意:它不阻塞,也不重试,是否重试由你控制——别默认循环调用,可能引发自旋浪费
在初始化、回调、事件触发等场景中怎么写才不重复执行
典型场景如:服务启动时加载配置、HTTP 请求首次到来时初始化缓存、WebSocket 连接建立后注册监听器。这些都要求“只做一次”,且可能被多个线程并发触发。
错误写法:if (!initialized) { init(); initialized = true; } —— 这里 initialized 是普通 boolean,读写非原子,必然竞态。
一套面向小企业用户的企业网站程序!功能简单,操作简单。实现了小企业网站的很多实用的功能,如文章新闻模块、图片展示、产品列表以及小型的下载功能,还同时增加了邮件订阅等相应模块。公告,友情链接等这些通用功能本程序也同样都集成了!同时本程序引入了模块功能,只要在系统默认模板上创建模块,可以在任何一个语言环境(或任意风格)的适当位置进行使用!
- 正确模式是:声明
private final AtomicBoolean initialized = new AtomicBoolean(false) - 执行点写成
if (initialized.compareAndSet(false, true)) { init(); } - 不要在
init()外再设值;也不要反过来写compareAndSet(true, false),语义混乱 - 如果
init()可能抛异常,记得捕获后把标志位重置(谨慎!仅当明确允许重试时),否则状态会卡死
compareAndSet() 和 getAndSet() 别混用
前者是条件更新(“如果是 false,就改成 true”),后者是无条件覆盖(“不管原来是啥,先取旧值,再设成 true”)。在“单次执行”需求里,后者完全失效——两个线程同时调用 getAndSet(true),都会拿到 false 并继续执行。
-
compareAndSet(false, true):安全,推荐 -
getAndSet(true):危险,不能用于防重 -
lazySet(true):不保证可见性顺序,可能让其他线程长期看不到更新,别用在控制逻辑上 - 别用
weakCompareAndSet()(已废弃),JDK9+ 已移除,行为不可靠
容易被忽略的边界:初始化失败后的状态管理
最常被跳过的点是:init() 抛异常了,AtomicBoolean 却已经变成 true,后续所有调用都直接跳过,导致功能永久不可用。
- 如果初始化允许失败重试,应在 catch 块中调用
initialized.set(false),但要注意这会开放给其他线程再次尝试 - 更稳妥的做法是用
AtomicReference<throwable></throwable>记录失败原因,配合compareAndSet(null, ex)实现“首次失败即锁定” - 别依赖
AtomicBoolean自身表达“执行中/成功/失败”三种状态——它只有两种值,多状态得组合其他变量
真正难的不是写对那行 compareAndSet(),而是想清楚:失败算不算“已执行”?要不要重试?重试时有没有资源残留要清理?这些逻辑一漏,原子性再强也没用。









