Java 引用更新是线程安全的吗?
定期更新myobjJava 引用更新是线程安全的吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,定期更新myobj 其他类使用getData获取数据 这段代码在不使用volatile关键字的情况下是线程安全的吗 我想是的。有人能确认吗?不,没有 如果没有volatile,从不同线程调用getData()可能会返回过时的缓存值。 volatile强制一个线程的分配立即在所有其他线程上可见 请注意,如果对象本身不是不可变的,则可能存在其他问题。否,这不是线程安全的。(你怎么会这么认为?) 如果要更新一个线程中的变量并从另一个线程读取该变量,则必须在写入和后续读取之间建立一个连接 简而言之,这基
其他类使用getData获取数据
这段代码在不使用volatile关键字的情况下是线程安全的吗
我想是的。有人能确认吗?不,没有 如果没有
volatile
,从不同线程调用getData()
可能会返回过时的缓存值。volatile
强制一个线程的分配立即在所有其他线程上可见
请注意,如果对象本身不是不可变的,则可能存在其他问题。否,这不是线程安全的。(你怎么会这么认为?) 如果要更新一个线程中的变量并从另一个线程读取该变量,则必须在写入和后续读取之间建立一个连接 简而言之,这基本上意味着使读取和写入
同步(在同一个监视器上),或使引用不稳定
如果没有这一点,就无法保证读取线程将看到更新,而且它甚至不会像“那么,它将看到旧值或新值”那样简单。您的读者线程可能会看到一些非常奇怪的行为,以及随之而来的数据损坏。例如,看看这篇文章的评论,尤其是布莱恩·戈茨的评论是如何值得一读的:
这个故事的寓意是:每当可变数据在线程之间共享时,如果您没有正确地使用同步(这意味着使用公共锁来保护对共享变量的每次访问,无论是读还是写),您的程序都会被破坏,并且以您可能无法枚举的方式被破坏
你可能会得到一个过时的参考资料。您可能无法获得无效的引用。
得到的引用是对变量指向、指向或将指向的对象的引用值
请注意,无法保证引用的过期程度,但它仍然是对某个对象的引用,并且该对象仍然存在。换句话说,写入引用是原子的(写入过程中不会发生任何事情),但不是同步的(它受到指令重新排序、线程本地缓存等的影响)
如果将引用声明为volatile
,则在变量周围创建一个同步点。简单地说,这意味着访问线程的所有缓存都被刷新(写入被写入,读取被遗忘)
只有
long
和double
两种类型没有原子读/写,因为它们在32位机器上大于32位。这种代码的最大问题是延迟初始化。如果没有volatile
或synchronized
关键字,您可以为尚未完全初始化的myobj
分配一个新值。Java内存模型允许在对象构造函数返回后执行部分对象构造。内存操作的这种重新排序正是内存屏障在多线程情况下如此关键的原因
如果没有内存屏障限制,就不会有“先发生后保证”,因此您不知道MyObj
是否已完全构建。这意味着另一个线程可能正在使用一个部分初始化的对象,并产生意外的结果
以下是有关构造函数同步的更多详细信息:
如果
MyObj
是不可变的(所有字段都是最终字段),则不需要volatile。volatile适用于布尔变量,但不适用于引用。Myobj的性能似乎像一个缓存对象,它可以与原子引用一起工作。因为您的代码从DB中提取值,所以我将让代码保持原样,并向其添加原子引用
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;
}
}
导入java.util.concurrent.AtomicReference;
公共类原子引用测试{
私有原子引用myobj=新原子引用();
公共类更新程序扩展线程{
公开募捐{
MyObj newMyobj=getNewObjFromDb();
updateMyObj(newMyobj);
}
公共无效更新MyObj(MyObj newMyobj){
myobj.compareAndSet(myobj.get(),newMyobj);
}
}
公共MyObj getData(){
返回myobj.get();
}
}
MyObj类{
}
在我的情况下,获取陈旧数据是可以的。但我不想得到任何异常。现在,这种情况下是线程安全的吗?您希望出现哪种异常?myobj只能是null或另一个值,这两个值都是myobj的良好返回值。您需要处理空返回,但从这个意义上说,它是“线程安全的”。ConcurrentModifications或某些concurrancy异常。当更新程序更新引用时,可以尝试读取数据。如果调用程序thred获得过时的日期,则可以使用它me@Sebastian我不太确定。如果引用是非易失性的,JLS是否保证它将以原子方式更新?对于非易失性long
值,非同步读取器可以看到“撕裂”,其中一个单词来自旧值,一个单词来自新值。我几乎可以肯定的是,JLS不会保证在这种情况下您将获得“旧值或新值”。任何行为,包括致命的热点错误,都可能导致。@Sebastian如Brian Goetz在我的回答中的链接所说:“除非你精通并发,否则在没有同步的情况下(即使在只读路径上),你对“会出什么问题”的直觉是错误的。”通常是不完整的,关于事情发生顺序的推理非常复杂,你几乎肯定会错。”推荐书:,它详细解释了这在Java中是如何工作的。如果读取线程得到陈旧的数据,这很好。我是否仍然需要使用volatile.Yes。如果您没有使用适当的同步,那么代码的行为是未定义的。那么,如果我对陈旧数据(如我的情况)没有意见,那么不使用v可以吗
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 {
}