为什么这个java类不是Thread安全的。
class TestClass {
private int x;
int get() {
return x;
}
void set(int x) {
this.x = x;
}
}
我读到需要关键字synchronized
才能使其线程安全?毕竟不是在原子内完成的操作吗?
答案 0 :(得分:11)
虽然赋值本身是一个原子操作,但由于硬件和编译器的实现不同,不同的线程可能会看到成员x的不同值。即,由于某种缓存,一个线程的修改对于另一个线程可能是不可见的。这通常称为线程可见性问题。
您可以通过在监视器上同步(使用synchronized关键字或java.util.concurrent锁)或声明x为volatile来正确同步代码。
答案 1 :(得分:8)
对于多个处理器,某些值可能会被处理器缓存,并且可能无法反映其他线程/处理器对相同对象所做的更改。实际上,即使使用单个处理器,也可以实现JVM以这种方式工作。
语言规范明确要求同步方法提供内存屏障,并要求重新读取内存中的所有实例变量。
因为您的代码未同步,所以一个线程可以设置该值,但另一个线程将返回该线程仍然缓存的值。
请阅读Java语言规范的'Memory and Locks'章节。
答案 2 :(得分:5)
因为字段'x'未声明为volatile,所以不要求JVM确保'x'对所有其他线程可见。即如果一个线程不断读取'x'的值并且另一个线程正在写它,那么读取线程可能永远不会“看到”值的变化。
不需要synchronized关键字,但它会起作用,因为它会创建必要的内存屏障/缓存刷新以确保'x'可见,但在这种情况下使用volatile关键字会更有效。
答案 3 :(得分:1)
当你有两个修改/访问非易失性变量的方法时,它绝不是线程安全的。如果您想要只使用一种方法,可以尝试:
synchronized int getAndSet(int x, boolean set) {
if (set) this.x = x;
return this.x; // param x is for set
}