Volatile关键字:是我在两个线程同步中使用的变量吗?

时间:2012-09-13 07:00:04

标签: java multithreading synchronization volatile

我有一个类似下面的代码,其中一个对象在两个线程(主线程和Monitor线程)之间共享。我是否必须全局声明MyObject并将其设为volatile以确保它将被推送到内存中?否则,if语句可以打印“非空”,如果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上编写了它。将其视为伪代码和实际代码的混合。

4 个答案:

答案 0 :(得分:2)

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

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

 b = null; // doesn't affect a

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

因此,如果您想在线程之间共享状态,则必须在 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

[编辑] 我上面说的不正确。相反,将volatilelong一起使用可以正常运行Java 5及更好。这是确保此类型的原子读/写的唯一方法。请参阅此问题以获取参考:Is there any point in using a volatile long?

Kudos转到Affe指出这一点。感谢。

答案 1 :(得分:1)

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

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

IBM developerWorks已就此主题发表a useful article

答案 2 :(得分:1)

您的示例只包含一个线程Monitor,该线程在main()中创建并运行。

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

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

public synchronized void doSomethingWithObject(MyObject obj)

或创建同步块:

obj = null;

this.obj=obj;

答案 3 :(得分:1)

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