getter 和 setter 是否应该同步?

2022-08-31 13:50:20
private double value;

public synchronized void setValue(double value) {
    this.value = value;
}
public double getValue() {
    return this.value;
}

在上面的例子中,使 getter 同步有什么意义吗?


答案 1

我认为最好在这里引用Java并发实践

假设仅在写入共享变量时才需要使用同步是一个常见的错误;这根本不是真的。

对于可由多个线程访问的每个可变状态变量,对该变量的所有访问都必须使用相同的锁执行。在本例中,我们说变量由该锁保护。

在没有同步的情况下,编译器、处理器和运行时可能会对操作的执行顺序执行一些非常奇怪的事情。试图推断内存操作“必须”在完全同步的多线程程序中发生的顺序几乎肯定是不正确的。

通常,您不必对基元如此小心,因此,如果这将是一个或一个,则可能是:intboolean

当线程在没有同步的情况下读取变量时,它可能会看到一个过时的值,但至少它看到一个值实际上是由某个线程而不是某个随机值放置在那里的。

但是,对于 64 位操作,这是不正确的,例如 on 或 如果它们未声明:longdoublevolatile

Java 内存模型要求抓取和存储操作是原子的,但对于非易失性长变量和双精度变量,允许 JVM 将 64 位读取或写入操作视为两个单独的 32 位操作。如果读取和写入发生在不同的线程中,因此可以读取非易失性长,并取回一个值的高32位和另一个值的低32位。

因此,即使您不关心过时的值,在多线程程序中使用共享的可变长整型变量和双精度变量也是不安全的,除非它们被声明为易失性或由锁保护。


答案 2

让我通过示例向您展示JIT编译代码的合法方法是什么。你写道:

while (myBean.getValue() > 1.0) {
  // perform some action
  Thread.sleep(1);
}

JIT 编译:

if (myBean.getValue() > 1.0) 
  while (true) {
    // perform some action
    Thread.sleep(1);
  }

在稍微不同的情况下,甚至Java编译器也可以自豪地提供类似的字节码(它只需要消除动态调度到不同字节码的可能性)。这是一个教科书式的吊装示例。getValue

为什么这是合法的?编译器有权假设在执行上述代码时,结果永远不会改变。如果没有,则允许忽略其他线程的任何操作。myBean.getValue()synchronized


推荐