Java中对象的初始化是线程安全的吗
我在我的一个应用程序中编写了类似的代码,但我不确定它是否是线程安全的Java中对象的初始化是线程安全的吗,java,multithreading,object,initialization,Java,Multithreading,Object,Initialization,我在我的一个应用程序中编写了类似的代码,但我不确定它是否是线程安全的 public class MyClass { private MyObject myObject = new MyObject(); public void setObject(MyObject o) { myObject = o; } public MyObject getObject() { return myObject; } } 不同的线程将
public class MyClass {
private MyObject myObject = new MyObject();
public void setObject(MyObject o) {
myObject = o;
}
public MyObject getObject() {
return myObject;
}
}
不同的线程将调用
setObject()
和getObject()
方法。getObject()
方法将由一个线程调用,该线程将继续在画布上绘制。为了获得最佳FPS和平滑运动,我不希望线程一直等待同步锁。因此,我希望避免使用同步,除非它确实是必要的。那么这里真的有必要吗?或者有没有其他更好的方法来解决这个问题
顺便说一句,线程是否接收到对象的旧副本并不重要。至于当前版本的状态,它肯定不是线程安全的,因为并发访问
myObject
将建立数据竞争
您没有指定这一点,但是如果MyObject
不是线程安全的itsef,那么您的程序将不会是线程安全的,无论您对显示的代码做了什么
线程是否接收到对象的旧副本并不重要
Java内存模型允许通过数据竞争访问的对象发生比这更糟糕的事情:
- 线程可能总是接收相同的对象(它碰巧读取的第一个对象)李>
- 线程可能只观察初始化了一些可到达值的对象(撕裂的对象)
但是,您的案例甚至不需要锁:只要将实例变量
设置为volatile
,就足以保证线程间对象的安全共享。是的,在上述条件下是线程安全的,只要您将myObject
标记为volatile
。您将始终从getObject()
获取正确的MyObject
实例。不,这不是线程安全的-可能发生这种情况:
- 当调用
时,线程可能看不到最新版本的getObject
(您可以使用它)myObject
- 当调用
getObject
- 线程可能会看到对处于不一致状态(例如部分构造)的
的更新引用MyObject
解决这些问题的最简单方法是将myObject标记为volatile。实际上,这里有许多复杂问题:
您应该将共享变量
设置为volatile
,以便让线程知道其他线程/进程/etc可能会更改其值
除此之外,代码中没有并发问题
第二行在创建MyClass
的实例时创建MyObject
的实例,这非常好。在MyObject
的实例完全构造完成之前,没有人可以访问共享变量(除非从构造函数中泄漏共享变量)
setObject
方法也很好-它所做的只是将一个对象分配给共享变量myObject
。因为赋值是原子的,所以没什么好担心的。在访问MyClass之前,myObject的初始值将被完全构造。
——如果myObject
不是最终值,则这不是真的。不过,如果在Java9中修改Java内存模型的计划成功了,它将成为现实。错误的当实例尚未创建时,您无法调用getObject。如果MyClass的实例是通过数据竞赛发布的,则可能会看到myObject处于部分构造状态。+1如果没有volatile
,JIT可以内联一个值,这意味着该字段永远不会被再次读取。是,这是MyClass上的数据竞赛,而不是myObject上的数据竞赛。查看所有其他答案。由于缺少易失性,这是不安全的。“我想避免使用同步,除非它确实是必要的”-您评估了同步开销吗?例如,每秒重新绘制画布30次,因此每33毫秒调用一次getObject()。同步(只需锁定,无需等待())所需时间不到一微秒。是的,谢谢您的提醒。我并没有实际评估会牺牲多少时间。但是我将使用volatile
而不是同步,因为在这种情况下,看到所有其他回复就足够了。线程如何看到myObject
有一个部分构造的实例?赋值是在对象实例化之后完成的,赋值本身是原子的,不是吗?@dcastro nop-想象一个线程是这样做的:MyObject o=new MyObject();设置对象(o)代码>第一条语句包括:分配内存、分配对o
的引用、构造对象。理论上,从另一个线程来看,这3个操作和下面的语句可以以不同的顺序出现,例如在构建对象之前调用setObject
。除非你在两个线程之间引入一个正确的before关系,那就是。啊,这是一个很好的观点。因此,在发布参考之前需要一个发布围栏。我不知道JMM允许这样的重新排序。非常感谢您的帮助!我会让变量在你输入的时候变为易变的