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
根据我在网上看到的一些文档,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