java - AtomicBoolean 是否保证 "safe-thread"?

标签 java multithreading atomic

根据我在网上看到的一些文档,Atomic 类的变量如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 是如何工作的。

谁能帮帮我?

最佳答案

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

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

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

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

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

简答题

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

日志的顺序给人的印象是 T1 没有读取 atomicBoolean 的当前值,而这是因为在执行 System.out.println 时可能发生的线程交错。

详细顺序

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

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 为 false 并且关于调用或刚刚进入 System.out.println 方法但尚未到达 synchronized(this) 语句因此没有锁定尚未在 this 上获得。

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

现在 T1 从它上次离开的地方恢复执行,即 System.out.println。但请记住,它尝试打印的 String 的值已经构建并且其值为 T1 正在等待 T3 将 Atomic 设置为 true。当前为假。所以现在 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

关于java - AtomicBoolean 是否保证 "safe-thread"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36193183/

相关文章:

multithreading - Delphi-线程中的消息泵未接收到WM_COPYDATA消息

c++ - 如何仅在文件不存在时创建文件?

.net - 仅使用原子操作的 C++/CLI 中的线程同步

java - 使用 try/catch 的正确方法

java - 在java中查找两个集合中的唯一点和公共(public)点,而不使用任何集合、列表等

Java在线程完成之前执行下一个任务

performance - 平均而言,现代 x64 CPU cmpxchg16b 比 64 或 32 位 CPU 慢得多?

Java cucumber : Specify target tags in the definition steps

java - 为什么我仍然无法找到 Java SE 运行时环境?

python - (Python) 在线程中关闭 pygame.display