我的班级是线程安全的吗?如果不是为什么?
class Foo {
boolean b = false;
void doSomething() throws Exception {
while (b) Thread.sleep();
}
void setB(boolean b) {
this.b = b;
}
}
我的班级是线程安全的吗?如果不是为什么?
class Foo {
boolean b = false;
void doSomething() throws Exception {
while (b) Thread.sleep();
}
void setB(boolean b) {
this.b = b;
}
}
代码不是线程安全的,因为正在运行的线程可能会在代码编译之前看到更改(稍后可能是随机点)并且您不再看到更改。
顺便说一句:这使得测试变得非常困难。例如,如果您睡 1 秒钟,您可能会在将近三个小时内看不到这种行为。
即它可能会或可能不会起作用,你不能说它已经起作用了,它会继续起作用。
因为JIT 不能也不会b
优化volatile
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
to更改false
,而是永远循环。这显然是不正确的行为。而且由于这仅在有两个线程时才会出现,因此这是一个线程安全问题。
这里有一个有争议的概念是可见性,在 Concurrent Java 中讨论过。如果 doSomething() 在不同的线程上运行,JVM 不保证在 setB 中执行的操作将适用于 doSomething 看到的 b 版本。所以你可以调用 setB 并且 doSomething 不能保证永远终止。