Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中对象的初始化是线程安全的吗_Java_Multithreading_Object_Initialization - Fatal编程技术网

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内存模型允许通过数据竞争访问的对象发生比这更糟糕的事情:

  • 线程可能总是接收相同的对象(它碰巧读取的第一个对象)
  • 线程可能只观察初始化了一些可到达值的对象(撕裂的对象)
为了获得最佳FPS和平滑运动,我不希望线程一直等待同步锁

您是否花费了一些精力来实际测量线程等待锁的时间?我猜:你没有,因为时间太短,无法察觉


但是,您的案例甚至不需要锁:只要将实例变量
设置为volatile
,就足以保证线程间对象的安全共享。

是的,在上述条件下是线程安全的,只要您将
myObject
标记为
volatile
。您将始终从
getObject()

获取正确的
MyObject
实例。不,这不是线程安全的-可能发生这种情况:

  • 当调用
    getObject
    时,线程可能看不到最新版本的
    myObject
    (您可以使用它)
  • 当调用
    getObject
  • 线程可能会看到对处于不一致状态(例如部分构造)的
    MyObject
    的更新引用

解决这些问题的最简单方法是将myObject标记为volatile。

实际上,这里有许多复杂问题:

  • myObject需要是易变的。(否则,其他线程可能永远看不到更改)

  • myObject的初始值将在访问MyClass之前完全构造,因此在这种情况下是安全的,但是一般来说,您需要注意将对象构造和多线程结合起来


  • 您应该将共享变量
    设置为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允许这样的重新排序。非常感谢您的帮助!我会让变量在你输入的时候变为易变的