Java类中的线程安全性

18

为什么这个Java类不是线程安全的。

class TestClass {  
   private int x;

   int get() {
       return x;
   }

   void set(int x) {
       this.x = x;
   }  
}

我读到了关键字synchronized是用来使代码线程安全的?毕竟操作不是在原子级别执行吗?

4个回答

14

尽管赋值本身是原子操作,但由于不同的硬件和编译器实现,不同的线程可能会看到成员变量x的不同值。也就是说,一个线程的修改对另一个线程来说可能是不可见的,因为有一些缓存机制。这通常被称为线程可见性问题。

您可以通过在监视器上同步(使用synchronized关键字或java.util.concurrent锁)或将x声明为volatile来适当地同步代码。


在C#中也是这样吗? - softwarematter

9

在多处理器系统中,某些值可能会被处理器缓存,并且可能不反映其他线程/处理器对相同对象所做的更改。实际上,即使只有一个处理器,JVM也可以实现这种工作方式。

语言规范明确要求同步方法呈现内存屏障,并要求从内存重新读取所有实例变量。

因为您的代码未同步,因此一个线程可能会设置该值,但另一个线程将返回仍由该线程缓存的值。

请阅读Java语言规范的'Memory and Locks'章节。


6
由于字段“x”没有声明为易失性,因此JVM没有要求确保“x”对所有其他线程可见。也就是说,如果一个线程不断读取“x”的值,另一个线程正在写入它,那么读取线程可能永远无法“看到”值的变化。
虽然不需要使用synchronized关键字,但是可以使用它来创建必要的内存屏障/缓存刷新以确保“x”可见,但在这种情况下使用volatile关键字将更有效率。

0

当你有两个方法修改/访问一个非易失性变量时,它永远不是线程安全的。如果你想只有一个方法,你可以尝试:

synchronized int getAndSet(int x, boolean set) {
    if (set) this.x = x;
    return this.x;   // param x is for set
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接