public class Test{
private MyObj myobj = new MyObj(); //it is not volatile
public class Updater extends Thred{
myobje = getNewObjFromDb() ; //not am setting new object
}
public MyObj getData(){
//getting stale date is fine for
return myobj;
}
}
定期更新MyOBJ
其他类使用GetData获取数据
此代码线程在不使用挥发性关键字的情况下安全吗?
我想是的。有人可以确认吗?
no ,这不是线程安全。(是什么让您认为是?)
如果您要在一个线程中更新变量并从另一个线程中读取它,则必须在写入和后续读取之间建立。
。简而言之,这基本上意味着制作读写和写入synchronized
(在同一监视器上),或进行参考volatile
。
没有这些,就无法保证阅读线程会看到更新 - 甚至不像"它可以看到旧值或新值"那么简单。您的读者线程可能会看到随后发生的数据损坏的一些非常奇怪的行为。查看缺乏同步会导致无限循环的缺乏(该文章的评论,尤其是Brian Goetz',非常值得阅读):
故事的道德:每当跨线程共享可变数据时,如果您不正确使用同步(这意味着使用公共锁来保护对共享变量的每个访问,读取或写入),则您的程序被损坏了,并以您甚至无法枚举的方式打破。
不,不是。
没有volatile
,从其他线程调用getData()
可能会返回一个陈旧的缓存值。
volatile
强制从一个线程分配,立即在所有其他线程上可见。
请注意,如果对象本身不是不可变的,那么您可能会有其他问题。
您 May 获取过时的参考。您可能不会获得无效的参考。您获得的参考是对变量指向或指向或将指向对象的引用的值。
请注意, no 保证参考可能是多少,但它仍然是对 some 对象的引用,该对象仍然存在。换句话说,写作参考是 aromic (写入过程中什么都没发生),但不是同步(它遵守指令重新排序,螺纹 - 本地cache等。)。
如果将引用声明为volatile
,则在变量周围创建一个同步点。简而言之,这意味着访问线程的所有缓存都被冲洗(写入写入并忘记了读取)。
唯一无法获得原子读/写入的类型是long
和double
,因为它们在32位机器上大于32位。
如果MyObj
是不变的(所有字段都是最终),则不需要挥发性。
这种代码的大问题是懒惰的初始化。没有volatile
或synchronized
关键字,您可以为尚未完全初始化的myobj
分配一个新值。Java内存模型允许在对象构造器返回后执行对象构建的一部分。对内存操作的重新排序是为什么内存屏障在多线程中如此至关重要的原因。
没有内存屏障的限制,就不会发生任何保证,因此您不知道MyObj
是否已完全构造。这意味着另一个线程可以使用具有意外结果的部分初始化对象。
这是有关构造函数同步的更多详细信息:
java中的构造函数同步
挥发性将适用于布尔变量,但对于参考文献不适用。MyOBJ似乎表现像一个缓存的对象,它可以使用原子重新使用。由于您的代码从DB中提取值,我将让代码保持原样,并将原子归因于其添加。
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest {
private AtomicReference<MyObj> myobj = new AtomicReference<MyObj>();
public class Updater extends Thread {
public void run() {
MyObj newMyobj = getNewObjFromDb();
updateMyObj(newMyobj);
}
public void updateMyObj(MyObj newMyobj) {
myobj.compareAndSet(myobj.get(), newMyobj);
}
}
public MyObj getData() {
return myobj.get();
}
}
class MyObj {
}