我的类线程安全吗?如果不是为什么?
class Foo {
boolean b = false;
void doSomething() throws Exception {
while (b) Thread.sleep();
}
void setB(boolean b) {
this.b = b;
}
}
代码不是线程安全的,因为正在运行的线程可能会看到更改,直到代码被编译(稍后可能会在随机点)并且您不再看到更改。
顺便说一句:这使得测试变得非常困难,例如,如果你睡了 1 秒钟,你可能在将近三个小时内看不到这种行为。
也就是说,它可能有效,也可能无效,你不能仅仅因为它已经工作就说它将继续工作。
由于b
不是volatile
JIT可以并且将会优化
while (b) Thread.sleep(N);
要成为
boolean b = this.b;
if (b) while (true) Thread.sleep(N);
因此,不会每次都读取 b
的值。
不是。 setB()
更新实例变量b
值,但未synchronized
。
多个线程可能会尝试在同一时间点执行setB()
方法可能会导致不可预测的结果。
您需要synchronize
方法(或)使用synchronize
块this
对象上锁定。
看看 AtomicBoolean。这意味着任何时候只有一个线程可以访问它。
但是,它与线程安全有什么关系?有点困惑。
它可以追溯到"线程安全"的定义。 维基百科是这样说的:
"线程安全是一种适用于多线程程序上下文的计算机编程概念。如果一段代码在多个线程同时执行期间正常运行,则该代码是线程安全的。
这里的关键点是功能正确。 如果您查看 Peter Lawrey 解释的场景,您会发现 JIT 编译可能会导致代码不会注意到b
的值何时从 true
更改为 false
,而是永远循环。 这显然是不正确的行为。 由于这仅在有两个线程时才表现出来,因此这是一个线程安全问题。
这里有一个问题的概念是可见性,在并发Java中讨论过。 如果 doSomething() 在不同的线程上运行,JVM 不保证在 setB 中执行的操作将应用于 doSomething 看到的 b 版本。 所以你可以调用setB,doSomething不能保证永远终止。