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获取数据
这个代码线程是否安全而不使用volatile关键字?
我想是的。有人可以证实吗?
答案 0 :(得分:6)
否,这不是线程安全的。 (是什么让你觉得它是?)
如果要更新一个线程中的变量并从另一个线程中读取它,则必须在写入和后续读取之间建立happens-before relationship。
简而言之,这基本上意味着同时进行读写synchronized
(在同一台监视器上)或制作参考volatile
。
没有它,就不能保证阅读线程会看到更新 - 它甚至不会像“好吧,它会看到旧值或新值”那样简单。您的读者线程可能会看到一些非常奇怪的行为,随之而来的是数据损坏。看看lack of synchronization can cause infinite loops如何(对那篇文章的评论,特别是Brian Goetz',非常值得一读):
故事的寓意:每当跨线程共享可变数据时,如果你没有正确使用同步(这意味着使用公共锁来保护对共享变量的每次访问,读或写),你的程序就会被破坏,你可能甚至无法枚举。
答案 1 :(得分:1)
不,不是。
如果没有volatile
,则从其他线程调用getData()
可能会返回陈旧的缓存值。
volatile
强制一个线程的分配立即在所有其他线程上可见。
请注意,如果对象本身不是不可变的,则可能还有其他问题。
答案 2 :(得分:0)
您可能获得过时的参考。您可能获得无效的引用。 您获得的引用是变量指向或指向或将指向的对象的引用值。
请注意,没有保证引用可能有多少陈旧,但它仍然是某些对象和该对象的引用< em>仍然存在。换句话说,编写引用是 atomic (在写入期间不会发生任何事情)但不是 synchronized (它受指令重新排序,线程局部缓存等)。
如果将引用声明为volatile
,则会在变量周围创建一个同步点。简单来说,这意味着刷新访问线程的所有缓存(写入写入并忘记读取)。
唯一没有获得原子读/写的类型是long
和double
,因为它们在32位计算机上大于32位。
答案 3 :(得分:0)
这种代码的一个大问题是延迟初始化。如果没有volatile
或synchronized
个关键字,您可以为尚未完全初始化的myobj
分配新值。 Java内存模型允许在对象构造函数返回后执行部分对象构造。这种内存操作的重新排序是内存屏障在多线程情况下如此重要的原因。
如果没有内存屏障限制,则不会发生事先保证,因此您不知道MyObj
是否已完全构建。这意味着另一个线程可能正在使用具有意外结果的部分初始化对象。
以下是有关构造函数同步的更多详细信息:
答案 4 :(得分:0)
如果MyObj
是不可变的(所有字段都是最终的),则不需要volatile。
答案 5 :(得分:0)
Volatile适用于布尔变量但不适用于引用。 Myobj似乎像一个缓存的对象,它可以使用AtomicReference。由于您的代码从数据库中提取值,因此我将让代码保持原样并将AtomicReference添加到其中。
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 {
}