【问题标题】:Volatile keyword: is the variable I am using among two threads synchronized?可变关键字:我在两个线程之间使用的变量是否同步?
【发布时间】:2012-09-06 05:33:35
【问题描述】:

我有一个类似下面的代码,其中一个对象在两个线程(主线程和监视器线程)之间共享。我是否必须全局声明 MyObject 并将其设为 volatile 以确保它将被推送到内存?否则if 语句可以打印“Not null”如果 MyObject 只被线程本地访问并且没有声明为 volatile,对吧?

public static void main(String[] args) {
    MyObject obj = MyObjectFactory.createObject();
    new Monitor(obj).start();
    Thread.sleep(500); 
    if(obj == null) 
        System.out.println("Null");
    else
        System.out.println("Not null");
}

public void doSomethingWithObject(MyObject obj) {
    obj = null;
}

private class Monitor extends Thread {
   public Monitor(MyObject obj) {
       this.obj=obj;
   } 
   public void run() {
       doSomethingWithObject(obj);
   }
}

注意:代码示例可能无法编译,因为我是在 Stackoverflow 上自己编写的。将其视为伪代码和真实代码的混合体。

【问题讨论】:

  • 使用可编译的代码示例更容易说明您要演示的内容:)
  • @Affe:抱歉,我在帖子中做了一个注释,解释了为什么它不能编译。 :-)

标签: java multithreading synchronization volatile


【解决方案1】:

实例是共享的,但对它的引用不是。示例:

 String a = "hello";
 String b = a;

 b = null; // doesn't affect a

a 和b 是对同一个实例的引用;更改一个引用对实例或对同一实例的任何其他引用没有影响。

因此,如果您想在线程之间共享状态,则必须在 inside MyObject 中创建一个字段,该字段必须为 volatile:

class MyObject { public volatile int shared; }


public void doSomethingWithObject(MyObject obj) {
    obj.shared = 1; // main() can see this
}

请注意,volatile 仅适用于某些类型(引用和除 long 之外的所有原语)。 由于这很容易出错,因此您应该查看 java.util.concurrent.atomic 中的类型.

[编辑] 我上面说的不正确。相反,使用 volatile 和 long 可以按预期在 Java 5 和更好的版本中工作。这是确保这种类型的原子读/写的唯一方法。请参阅此问题以获取参考:Is there any point in using a volatile long?

感谢Affe 指出这一点。谢谢。

【讨论】:

  • @Rox 仅适用于原始变量,无论其字节大小
  • volatile 这样可以工作,但对其进行读写不是原子的,因此您可能会得到意想不到的结果:stackoverflow.com/questions/3038203/…
  • 链接的问题与您的断言相反。过去肯定有一段时间,它对 64 位数据没有按预期工作,但在很多年前
  • @Affe:你是 100% 正确的;我确定了我的答案。很抱歉造成混乱。
  • 来自 JLS (17.7)(来自引用的链接):“volatile long 和 double 值的写入和读取始终是原子的。对引用的写入和读取始终是原子的,无论它们是否是实现为 32 或 64 位值。"
【解决方案2】:

您宁愿必须在对象上进行同步,以确保在 if 检查之前将其设置为 null。将其设置为 volatile 仅意味着更改将立即被其他线程“看到”,但很可能会在 doSomethingWithObject 调用之前执行 if 检查。

【讨论】:

    【解决方案3】:

    如果您希望您的对象以原子方式通过读取-更新-写入方案,volatile 不会删除它。你必须使用同步。

    Volatility 将确保变量不会被缓存在当前线程中,但它不会保护变量免于同时更新,因为变量可能会出现意外情况。

    IBM 的developerWorks 有a useful article 的主题。

    【讨论】:

      【解决方案4】:

      您的示例仅包含一个线程Monitor,它在main() 中创建和运行。

      “让它变得易失以确保它会被推送到内存中?” - 相反,当您将变量声明为volatile - 它确保它不会被“推送”(缓存)到线程本地内存,因为可能有其他线程会更改变量的值。

      为了确保您打印出正确的变量值,您应该同步方法doSomethingWithObject(将方法的签名更改为):

      public synchronized void doSomethingWithObject(MyObject obj)
      

      或在周围创建同步块:

      obj = null;
      

      和

      this.obj=obj;
      

      【讨论】:

        猜你喜欢
        • 2013-08-10
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-17
        • 2016-07-08
        • 1970-01-01
        • 2020-10-13
        相关资源
        最近更新 更多