并发访问下AtomicBoolean出现连续同方向输出的原因
连续同方向输出的根本原因
核心误解:打印语句的字段含义
你代码中打印逻辑的实现如下:
System.err.println(super.getName() + " "+ bool +"->" + top + ". i is " + i);
这里箭头左侧的bool是每个Toppler线程在构造时传入的固定常量属性,初始化后不会再改变,并不是翻转前的top变量值;箭头右侧才是代码执行到打印时刻的top实际值。你误以为箭头代表「top从左侧值翻转到右侧值」,实际上它只是把线程自身的固定布尔值、和当前时刻的top值做了字符串拼接。
分支逻辑漏洞
你的翻转操作是写在if判断内部的:
synchronized (top) { if (top.get() == bool) top.set(!top.get()); System.err.println(super.getName() + " "+ bool +"->" + top + ". i is " + i); }
这意味着:
- 只有当当前
top值和线程自身的bool相等时,才会执行翻转操作 - 当条件不满足时,代码完全不会修改
top的值,但因为打印语句写在if块外部,无论是否触发翻转,都会照常执行打印。
结合实际输出的逻辑复现
你观察到的连续同方向输出,完全可以通过上述逻辑解释:
假设某一刻top的值为true,此时连续抢到synchronized锁的两个线程都是bool=false的类型:
- 第一个bool=false的线程拿到锁,判断
top.get()=true,和自身的bool不相等,不执行翻转,直接打印[线程名] false->true(左侧是自己的固定bool值,右侧是当前top值true) - 第二个bool=false的线程拿到锁,此时top仍然是true,依然不满足翻转条件,同样打印
[线程名] false->true
最终就出现了连续两行false->true的输出,但实际上整个过程中top的值从未发生变化。
你输出中出现的连续true->false也是同理:当top值为false时,连续抢到锁的两个bool=true的线程,都因为当前top值和自身bool不相等、不执行翻转操作,直接拼出true->false的打印内容,看起来就像连续发生了两次相同方向的状态翻转。
补充说明
- 这里的synchronized锁已经完全保证了top操作的原子性与内存可见性,不存在AtomicBoolean工作机制异常的问题,本质是分支逻辑和打印设计的bug。
- 这段代码中的synchronized锁其实是冗余的:AtomicBoolean本身提供的
compareAndSet是原子操作,直接使用top.compareAndSet(bool, !bool)就可以完成条件翻转的逻辑,不需要额外加锁。
内容的提问来源于stack exchange,提问作者Andrew Levchenko
相关产品推荐
相关产品推荐

