getter方法是Java中volatile的替代方法吗?

时间:2017-04-08 12:12:49

标签: java multithreading volatile getter

我想知道私有布尔字段的getter方法是否强制其他线程获取最新的更新值?这是不稳定场的替代品吗? 例如:

Class A {
    private boolean flag;

    public boolean getFlag() {
        return this.flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

VS

Class B {
    public volatile boolean flag;
}

编辑:整个对象是否被线程(包括私有字段)缓存,这样当我调用getter时它会返回缓存的私有字段吗?

4 个答案:

答案 0 :(得分:3)

不,getter不会导致字段同步。

在谈论在多线程环境中读取和编写基元时,我们有三个问题,由CPU引起

  1. 原子性:可能需要存储或加载操作多个汇编指令,例如,在32位CPU上写入64位整数。当前线程可以在指令序列的中间由操作系统进入休眠状态。
  2. 可见性:在一个核心上运行的线程可能无法读取其他核心的其他线程写入的最新值。只是因为CPU这么说。
  3. 重新排序:为了使程序运行得更快,CPU会在其认为合适时混合装配说明的顺序。
  4. Getter没有解决任何这些问题。即使它是,JIT编译器也可以完全优化该功能。比什么?

    挥发性是解决上述问题的方法之一。锁定也是如此。它们确保一个线程读取基元的最新值,或确保正在写入的值对其他线程可见。它们还使组装指令完全按照编译方式运行,而不进行任何混合。

    作为旁注,生成的程序集可能与您在代码中编写的内容完全不同。你问自己“我在我的代码中写了flag来读,所以为什么程序不会从字段本身读取?”编译器可以做任何它认为合适的事情,以使组件尽可能快。通过添加任何锁或易失性说明符,您基本上告诉编译器不涉及多线程访问,并且编译器(以及随后的CPU)可以自由地假设对象未被触及多线程。可能是这个对象可能不会在第一时间创建。 JIT编译器可能会说“好吧,在寄存器中声明这个布尔值并将其视为整个对象”。很有可能。

      

    编辑:整个对象是否被线程(包括私有字段)缓存是真的,所以当我调用getter时它会返回缓存的私有字段吗?

    你无法想象。它取决于JVM,底层操作系统和底层CPU。它可以完全缓存,部分缓存或根本不缓存。提醒你,大多数CPU都有多个缓存行,即使对象被缓存,缓存在哪里?在寄存器或其中一个缓存行?

答案 1 :(得分:1)

  

我想知道私有布尔字段的getter方法是否强制其他线程获取最新的更新值

没有。在没有volatile关键字的情况下,它不会强制其他线程获取最新值。

  

这是不稳定领域的替代方案吗?

没有。在没有boolean关键字的情况下,简单的getter调用不能替代获得volatile值的最新值。

解决问题的更好解决方案:使用AtomicBoolean

  

可以原子方式更新的布尔值。有关原子变量属性的描述,请参阅java.util.concurrent.atomic包规范。

几个有用的链接:

Atomic package-summary

What is the difference between atomic / volatile / synchronized?

答案 2 :(得分:0)

  

getter强制线程获取未缓存的值?这是不稳定场的替代方案吗?

不,getter方法不会强制执行任何操作,因此您必须需要volatile,以便当前线程看到其他线程更新的最新值,否则,您可能会看到过时的值。 / p>

您需要了解here中的以下文字,并明确了解原子访问的概念。

  

volatile变量始终对其他线程可见。它   也意味着当一个线程读取一个volatile变量时,它看不到   只是对易变的最新变化

答案 3 :(得分:0)

如果您在这两种方法中使用了 synchronized 关键字,则是:

public synchronized boolean getFlag() {
    return this.flag;
}

public synchronized void setFlag(boolean flag) {
    this.flag = flag;
}

那么这两个方法就相当于在实例变量上使用了volatile