【发布时间】:2021-03-30 05:55:39
【问题描述】:
我得到了这样的代码:
public class ConcurrencyCheck {
private volatile static AtomicBoolean top=new AtomicBoolean(false);
private int i=0;
public class Toppler extends Thread{
private final boolean bool;
public Toppler(boolean myBool,String name) {
super(name);
bool=myBool;
}
@Override
public void run() {
while(!isInterrupted()){
i++;
synchronized (top) {
if (top.get() == bool) top.set(!top.get());
System.err.println(super.getName() + " "+ bool +"->" + top + ". i is " + i);
}
try {
sleep(100);
} catch (InterruptedException e) {
break;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
ConcurrencyCheck cc = new ConcurrencyCheck();
Thread t1= cc.new Toppler(true,"thread1");
Thread t2= cc.new Toppler(false,"thread2");
Thread t3= cc.new Toppler(true,"thread3");
Thread t4= cc.new Toppler(false,"thread4");
Thread t5= cc.new Toppler(true,"thread5");
Thread t6= cc.new Toppler(false,"thread6");
Thread t7= cc.new Toppler(true,"thread7");
Thread t8= cc.new Toppler(false,"thread8");
t1.start();
...
t8.start();
sleep(950);
t1.interrupt();
...
t8.interrupt();
}
}
它旨在检查 AtomicBoolean 的工作原理。类 Toppler 是一个定期推翻 Atomic 布尔值的线程。推翻布尔值的代码块是同步的。正如我猜想的那样,每个输出行都必须推翻“top”变量的值,所以输出必须是“true->false false->true true->false ...”。但出于某种原因,有时我会看到这样的输出:
thread1 true->false. i is 1
thread8 false->true. i is 8
thread7 true->false. i is 8
thread4 false->true. i is 8
thread6 false->true. i is 8
thread3 true->false. i is 8
thread5 true->false. i is 8
thread2 false->true. i is 8
thread1 true->false. i is 9
thread8 false->true. i is 10
thread7 true->false. i is 11
thread4 false->true. i is 12
thread6 false->true. i is 13
thread3 true->false. i is 14
问题是:为什么两个后续的 false->true 或 true->false 输出行是可能的?
【问题讨论】:
-
您对“top”和“toppler”这两个词的使用令人困惑。你什么意思?如果该值与任务构造函数的给定布尔值匹配,您的目标是每个任务翻转布尔值吗?
-
因为线程乱序运行时它没有翻转。
if条件没有通过,但打印输出令人困惑,假设它会通过。 -
是的。 Toppler 是一个反布尔值的类,如果它等于在构造 Toppler 类时设置的 'bool' 变量。
-
“我错过了括号” - 这是许多静态代码检查器总是需要使用括号的原因之一。你可以很容易地产生这种情况,所以我建议为你自己(和你的团队)制定一个规则,这样一个块上缺少的括号就会在你的脑海中引发一个危险信号。
-
“我总是在 if 块中使用括号。” - 这是一个好的开始,但我建议您将始终使用它们来防止与其他构造(例如循环)发生相同情况的规则。单表达式 lambda 可能是一个例外,因为无论如何编译器都会强制您为超过 1 个语句添加大括号。
标签: java concurrency