根据我在互联网上阅读的一些文档,像AtomicInteger
,AtomicLong
这样的类Atomic的变量只允许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
的工作原理。
有人能帮助我吗?
答案 0 :(得分:3)
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将原子设置为真实
线程T1 调用sysout 进行打印 - >等待T3设置Atomic为true。当前是错误的(仅称为 sysout方法,但锁定尚未获得)
线程T3打印 - >打印T3真实
线程T1 sysout完成并打印 - >等待T3设置Atomic为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
上尚未获得锁定。< / p>
此时可能会发生T3轮流继续执行并将atomicBoolean
转换为true
并使用T3 true
打印行System.out.println()
,即获取并释放锁定(在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&amp; 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
答案 1 :(得分:0)
您可能希望尝试阅读https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html
上的官方文档,而不是参考某些文档&#34;考虑原子包中的类的一种快速简便的方法是,每个方法(如get,set,compareAndSet)都表现得好像是同步的(忘记了懒惰和弱方法)。