便宜的读写锁中的冗余易失性?

时间:2018-05-11 18:44:48

标签: java multithreading concurrency volatile

Brian Goetz来自https://www.ibm.com/developerworks/java/library/j-jtp06197/

的文章

使用下面粘贴的示例作为廉价的读写锁。我的问题是,如果int变量值没有声明为volatile,那么它会产生影响吗?我的理解是,由于对值的写入是在同步块内完成的,因此任何方式对其他线程都可以看到最新值,因此声明它是易失性的。请澄清?

@ThreadSafe
public class CheesyCounter {
// Employs the cheap read-write lock trick
// All mutative operations MUST be done with the 'this' lock held
@GuardedBy("this") private volatile int value;

public int getValue() { return value; }

public synchronized int increment() {
    return value++;
}

}

3 个答案:

答案 0 :(得分:0)

public synchronized int increment()

如果两个或更多线程试图同时递增(因为synchronized不是原子的),这个++会阻止你跳过增量。

private volatile int value

这可以防止您在另一个线程中已经增加的一个线程中看到过时的值。 (请注意,我们也可以使getValue同步以实现此目的)

答案 1 :(得分:0)

  

我的理解是,由于对值的写入是在同步块内完成的,因此任何方式都可以看到其他线程的最新值

这是不正确的。通常,一旦进行更改,就无法保证其他线程“看到”对变量的更改。线程可能会看到已更改变量的陈旧值,例如,线程看到的值实际上是在寄存器而不是主存储器中。

volatile变量建立“之前发生”的语义。 JLS, section 17.4.5,声明:

  

发生在之前的关系可以订购两个动作。如果一个动作发生在另一个动作之前,那么第一个动作在第二个动作之前是可见的并且在第二个动作之前被命令。

     
      
  • volatile字段(§8.3.1.4)的写入发生在该字段的每次后续读取之前。
  •   

JLS, Section 8.3.1.4

  

字段可以声明为volatile,在这种情况下,Java内存模型可确保所有线程都看到变量的一致值(第17.4节)。

该字段必须为volatile的原因是即使读取是原子的,它也需要确保该值是当前的 - 以前由另一个线程写入的任何值都是可见的。读取是原子的还不够; volatile仍然是必要的,以确保价值的一致性。

答案 2 :(得分:0)

非常感谢答案的人。现在也在oracle网站上找到了这个:“其次,当一个synchronized方法退出时,它会自动建立一个先前发生的关系,同时对同一个对象的同步方法进行调用。” https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html