我有一个类似下面的代码,其中一个对象在两个线程(主线程和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上编写了它。将其视为伪代码和实际代码的混合。
答案 0 :(得分: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
。
[编辑] 我上面说的不正确。相反,将volatile
与long
一起使用可以正常运行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检查。