将 volatile 关键字与可变对象一起使用

发布于 2024-10-10 11:33:10 字数 387 浏览 8 评论 0原文

在Java中,我知道易失性关键字提供了变量的可见性。问题是,如果变量是对可变对象的引用,那么 volatile 是否也提供该对象内部成员的可见性?

在下面的示例中,如果多个线程正在访问 易失性可变 m 并更改值,它是否可以正常工作?

例子

class Mutable {
    private int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

class Test {
    public volatile Mutable m;
}

In Java, I understand that volatile keyword provides visibility to variables. The question is, if a variable is a reference to a mutable object, does volatile also provide visibility to the members inside that object?

In the example below, does it work correctly if multiple threads are accessing volatile Mutable m and changing the value?

example

class Mutable {
    private int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

class Test {
    public volatile Mutable m;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

偏爱自由 2024-10-17 11:33:11

在您的示例中,volatile 关键字仅保证任何线程对“m”写入的最后一个引用对于随后读取“m”的任何线程都可见。

它不保证有关您的 get() 的任何信息。

因此,使用以下序列:

Thread-1: get()     returns 2
Thread-2: set(3)
Thread-1: get()    

返回 2 而不是 3 是完全合法的。volatile 不会对此进行任何更改。

但是,如果您将 Mutable 类更改为:

class Mutable {
    private volatile int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

那么就保证 Thread-1 中的第二个 get() 将返回 3。

但请注意 volatile< /code> 通常不是最好的同步方法。

在简单的获取/设置示例中(我知道这只是一个示例),像 AtomicInteger 这样的类,使用适当的同步并实际提供有用的方法会更好。

In your example the volatile keyword only guarantees that the last reference written, by any thread, to 'm' will be visible to any thread reading 'm' subsequently.

It doesn't guarantee anything about your get().

So using the following sequence:

Thread-1: get()     returns 2
Thread-2: set(3)
Thread-1: get()    

it is totally legitimate for you to get back 2 and not 3. volatile doesn't change anything to that.

But if you change your Mutable class to this:

class Mutable {
    private volatile int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

Then it is guaranteed that the second get() from Thread-1 shall return 3.

Note however that volatile typically ain't the best synchronization method.

In you simple get/set example (I know it's just an example) a class like AtomicInteger, using proper synchronization and actually providing useful methods, would be better.

Bonjour°[大白 2024-10-17 11:33:11

易失性仅提供对如此声明的对象的引用的保证。该实例的成员不会同步。

根据 维基百科,您有:

  • (在 Java 的所有版本中)读取和读取都有一个全局排序
    写入易失性变量。这
    意味着每个访问 a 的线程
    易失性字段将读取其当前值
    继续之前的值,而不是
    (可能)使用缓存值。
    (但是,不能保证
    挥发性的相对顺序
    与常规读取一起读取和写入
    并写道,这意味着它是
    通常不是有用的线程
    构造。)
  • (在 Java 5 或更高版本中)易失性读取和写入建立发生之前
    关系,就像获取和
    释放互斥锁。

所以基本上你所拥有的是,通过声明字段易失性,与其交互创建一个“同步点”,之后任何更改都将在其他线程中可见。但此后,使用 get()set() 是不同步的。 Java 规范有更全面的解释。

volatile only provides guarantees about the reference to the Object that is declared so. The members of that instance don't get synchronized.

According to the Wikipedia, you have:

  • (In all versions of Java) There is a global ordering on the reads and
    writes to a volatile variable. This
    implies that every thread accessing a
    volatile field will read its current
    value before continuing, instead of
    (potentially) using a cached value.
    (However, there is no guarantee about
    the relative ordering of volatile
    reads and writes with regular reads
    and writes, meaning that it's
    generally not a useful threading
    construct.)
  • (In Java 5 or later) Volatile reads and writes establish a happens-before
    relationship, much like acquiring and
    releasing a mutex.

So basically what you have is that by declaring the field volatile, interacting with it creates a "point of synchronization", after which any change will be visible in other threads. But after that, using get() or set() is unsynched. The Java Spec has a more thorough explanation.

如梦 2024-10-17 11:33:11

使用易失性而不是完全同步值本质上是一种优化。与同步访问相比,优化来自于为易失性值提供的较弱的保证。过早的优化是万恶之源;在这种情况下,邪恶可能很难追踪,因为它会以竞争条件等形式出现。因此,如果您需要询问,您可能不应该使用它。

Use of volatile rather than a fully synchronized value is essentially an optimization. The optimization comes from the weaker guarantees provided for a volatile value compared with a synchronized access. Premature optimmization is the root of all evil; in this case, the evil could be hard to track down because it would be in the form of race conditions and such like. So if you need to ask, you probably ought not to use it.

初见 2024-10-17 11:33:11

易失性不“提供可见性”。它的唯一作用是防止处理器缓存变量,从而提供并发读取和写入的先发生关系。它不会影响对象的成员,也不提供任何同步同步锁定。

由于您没有告诉我们您的代码的“正确”行为是什么,因此无法回答该问题。

volatile does not "provide visibility". Its only effect is to prevent processor caching of the variable, thus providing a happens-before relation on concurrent reads and writes. It does not affect the members of an object, nor does it provide any synchronisation synchronized locking.

As you haven't told us what the "correct" behaviour of your code is, the question cannot be answered.

雪化雨蝶 2024-10-17 11:33:10

这是对 volatile 的一些细节的旁注解释。写在这里是因为评论太多了。我想举一些例子来说明 volatile 如何影响可见性,以及它在 jdk 1.5 中如何变化。

给出以下示例代码:

public class MyClass
{
  private int _n;
  private volatile int _volN;

  public void setN(int i) {
    _n = i;
  }
  public void setVolN(int i) {
    _volN = i;
  }
  public int getN() { 
    return _n; 
  }
  public int getVolN() { 
    return _volN; 
  }

  public static void main() {
    final MyClass mc = new MyClass();

    Thread t1 = new Thread() {
      public void run() {
        mc.setN(5);
        mc.setVolN(5);
      }
    };

    Thread t2 = new Thread() {
      public void run() {
        int volN = mc.getVolN();
        int n = mc.getN();
        System.out.println("Read: " + volN + ", " + n);
      }
    };

    t1.start();
    t2.start();
  }
}

该测试代码的行为在 jdk1.5+ 中定义良好,但在 jdk1.5 之前定义良好。

在jdk1.5之前的世界中,易失性访问和非易失性访问之间没有定义的关系。因此,该程序的输出可能是:

  1. Read: 0, 0
  2. Read: 0, 5
  3. Read: 5, 0
  4. Read: 5, 5

在 jdk1.5+ 的世界中,易失性的语义发生了变化,因此易失性访问会影响非易失性访问。 - 易失性访问的方式与同步完全相同。因此,在 jdk1.5+ 世界中,只有某些输出是可能的:

  1. 读取:0, 0
  2. 读取:0, 5
  3. 读取:5, 0 <- 不可能
  4. 读取:5, 5

输出 3。这是不可能的,因为从易失性 _volN 读取“5”会在 2 个线程之间建立同步点,这意味着在分配给 _volN 之前从 t1 执行的所有操作必须对 t2 可见。

进一步阅读:

This is sort of a side note explanation on some of the details of volatile. Writing this here because it is too much for an comment. I want to give some examples which show how volatile affects visibility, and how that changed in jdk 1.5.

Given the following example code:

public class MyClass
{
  private int _n;
  private volatile int _volN;

  public void setN(int i) {
    _n = i;
  }
  public void setVolN(int i) {
    _volN = i;
  }
  public int getN() { 
    return _n; 
  }
  public int getVolN() { 
    return _volN; 
  }

  public static void main() {
    final MyClass mc = new MyClass();

    Thread t1 = new Thread() {
      public void run() {
        mc.setN(5);
        mc.setVolN(5);
      }
    };

    Thread t2 = new Thread() {
      public void run() {
        int volN = mc.getVolN();
        int n = mc.getN();
        System.out.println("Read: " + volN + ", " + n);
      }
    };

    t1.start();
    t2.start();
  }
}

The behavior of this test code is well defined in jdk1.5+, but is not well defined pre-jdk1.5.

In the pre-jdk1.5 world, there was no defined relationship between volatile accesses and non-volatile accesses. therefore, the output of this program could be:

  1. Read: 0, 0
  2. Read: 0, 5
  3. Read: 5, 0
  4. Read: 5, 5

In the jdk1.5+ world, the semantics of volatile were changed so that volatile accesses affect non-volatile accesses in exactly the same way as synchronization. therefore, only certain outputs are possible in the jdk1.5+ world:

  1. Read: 0, 0
  2. Read: 0, 5
  3. Read: 5, 0 <- not possible
  4. Read: 5, 5

Output 3. is not possible because the reading of "5" from the volatile _volN establishes a synchronization point between the 2 threads, which means all actions from t1 taken before the assignment to _volN must be visible to t2.

Further reading:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文