AtomicBoolean 是否保证 "safe-thread"?

Does AtomicBoolean guarantee "safe-thread"?

根据我在网上看到的一些文档,classAtomic的变量如AtomicInteger,AtomicLong,...只允许1个线程访问它们同一时间。但是当我尝试使用 AtomicBoolean 进行测试时,出了点问题。例如

public class TestAtomicBoolean {
    public static void main(String[] args) {
        final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

        new Thread("T1") {
            @Override
            public void run() {
                while (true) {
                    System.out.println(Thread.currentThread().getName() + " is waiting for T3 set Atomic to true. Current is " + atomicBoolean.get());
                    if (atomicBoolean.compareAndSet(true, false)) {                        
                        System.out.println("Done. Atomic now is " + atomicBoolean.get());
                        break;
                    }                    
                }
            }
        }.start();

        new Thread("T2") {
            @Override
            public void run() {
                while(true) {
                    System.out.println(Thread.currentThread().getName() + " " + atomicBoolean.get());                    
                }               
            }           
        }.start();

        new Thread("T3") {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " " + atomicBoolean.get());
                System.out.println(Thread.currentThread().getName() + " is setting atomic to true");
                atomicBoolean.set(true);
                System.out.println(Thread.currentThread().getName() + " " + atomicBoolean.get());
            }           
        }.start();                
    }
}

输出

T1 is waiting for T3 set Atomic to true. Current is false
T1 is waiting for T3 set Atomic to true. Current is false
T3 is setting atomic to true
T2 false
T3 true (*)
T1 is waiting for T3 set Atomic to true. Current is false (*)
T2 true
Done. Atomic now is false
T2 false

在第2行(*),虽然T3将AtomicBoolean设置为true,但之后T1读取的值为false。那么,T1 和 T3 同时访问 AtomicBoolean?我无法理解 AtomicBoolean 是如何工作的。

有人可以帮我吗?

与其参考 "some documents",不如尝试阅读 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html

上的官方文档

考虑原子包中 类 的一种快速简便的方法是,get、set、compareAndSet 等每个方法的行为都好像是同步的(忘记惰性和弱方法)。

AtomicBoolean 绝对是原子和线程安全的。

但在您的示例中,您试图测试 AtomicBoolean 的这种原子性质,它依赖于 System.out.println 打印具有误导性的日志。

因此,如果我们查看 System.out.println() 代码:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

我们将在上下文中使用上述 println() 方法查看事件流。

简答

线程 T1 打印 -> 正在等待 T3 将 Atomic 设置为 true。当前为 false
线程 T1 打印 -> 正在等待 T3 将 Atomic 设置为 true。当前为 false
线程 T3 打印 -> T3 正在将 atomic 设置为 true
线程 T1 调用 sysout 进行打印 -> 正在等待 T3 将 Atomic 设置为 true。当前为假(刚刚调用了 sysout 方法但尚未获取锁
线程 T3 打印 -> 打印 T3 true
线程 T1 sysout complete 并打印 -> is waiting for T3 set Atomic to true。当前为假

日志的顺序给人的印象是 T1 没有读取 atomicBoolean 的当前值,而这是因为在执行 System.out.println.

时可能发生的线程交错

详细顺序

应用程序以 false 的初始值开始 atomicBoolean

final AtomicBoolean atomicBoolean = new AtomicBoolean(false);

您输出的前两个日志如预期的那样来自 T1,并将 atomicBoolean 的值打印为 false。现在我们将忽略 T2 以简化,因为即使有两个线程我们也可以看到流程。

现在 T3 开始执行,即将 atomicBoolean 变为 true,如输出中所示。

T3 is setting atomic to true

并且在打印完上述行后,T1 立即有机会执行。此时atomicBoolean的值为false。因此 JVM 创建字符串 T1 is waiting for T3 set Atomic to true. Current is false 和关于调用或刚刚进入 System.out.println 方法但尚未到达 synchronized(this) 语句,因此尚未在 this 上获得锁定。

此时可能轮到 T3 继续执行并将 atomicBoolean 变为 true 并使用 System.out.println() 打印行 T3 true即,获取和释放锁(在 this)。

现在 T1 从它上次离开的地方恢复执行,即 System.out.println。但请记住,它尝试打印的 String 的值已经构建并且它的值为 T1 is waiting for T3 set Atomic to true. Current is false。所以现在 T1 打印这一行并继续。

有效地使用此流程,日志将如您观察到的那样。

T3 true
T1 is waiting for T3 set Atomic to true. Current is false

图示

下面是流程 w.r.t T1 和 T3 并且(试图)捕获上述讨论。 ----表示线程当前正在执行。空格表示它正在等待轮到它。

    1(false) 1(false)           1(false)just invoked   1(false)completed
T1 -------------------          ------                ------------------
T3                    ----------      ----------------  
                       2(false)        3(true)

LEGEND:
 1(false) - printing of T1 is waiting for T3 set Atomic to true. Current is false
 2(false) - printing of T3 is setting atomic to true
 3(true) - printing of T3 true