1

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

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

4 回答 4

2

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

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

 b = null; // doesn't affect a

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

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

[编辑]我上面说的不正确。相反,对于 Java 5 和更好的版本,使用volatilewith可以正常工作。long这是确保这种类型的原子读/写的唯一方法。请参阅此问题以获取参考:使用 volatile long 有什么意义吗?

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

于 2012-09-13T07:06:01.903 回答
1

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

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

IBM 的developerWorks有一篇关于这个主题的有用文章

于 2012-09-13T07:05:40.690 回答
1

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

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

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

public synchronized void doSomethingWithObject(MyObject obj)

或围绕以下内容创建同步块:

obj = null;

this.obj=obj;
于 2012-09-13T07:06:07.820 回答
1

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

于 2012-09-13T07:06:57.043 回答