在对象的构造函数完成之前关于对象的引用 [英] About reference to object before object's constructor is finished

查看:147
本文介绍了在对象的构造函数完成之前关于对象的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每个人都知道 JMM 的这个功能,有时候对象可能会在此对象的构造函数完成之前接收到值

Every one of you know about this feature of JMM, that sometimes reference to object could receive value before constructor of this object is finished.

JLS7,p 。 17.5 final Field Semantics 我们还可以阅读:


final 字段是一个简单的:为该对象的构造函数中的对象设置 final fields
; ,并且不要在正在另一个
线程在对象的构造函数完成之前看到它的地方写入一个
引用
。如果这个
被跟随,那么当对象被另一个线程看到时,
线程将总是看到正确构造的版本的
对象的 final 字段。 (1)

The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. (1)

在JLS之后,不能保证初始化非最终字段(1示例17.5-1.1)(2)

And just after that in JLS the example follows, which demonstrate, how non-final field is not guaranteed to be initialized (1Example 17.5-1.1) (2):

class FinalFieldExample { 
    final int x; 
    int y; 

    static FinalFieldExample f;

    public FinalFieldExample() { 
        x = 3; 
        y = 4; 
    } 

    static void writer() { 
        f = new FinalFieldExample(); 
    } 

    static void reader() { 
       if (f != null) { 
           int i = f.x; // guaranteed to see 3 
           int j = f.y; // could see 0 
       } 
    } 
}

,在问题答案中,Gray先生写道:

Also, in this question-answer Mr. Gray wrote:


如果您将该字段标记为 final ,那么构造函数将保证
完成初始化作为构造函数的一部分。否则,你将
在使用之前必须在锁上同步。 (3)

If you mark the field as final then the constructor is guaranteed to finish initialization as part of the constructor. Otherwise you will have to synchronize on a lock before using it. (3)






因此,问题是:

1)根据语句(1),我们应避免分享对 immutable <对象在其构造函数完成之前

1) According to statement (1) we should avoid sharing reference to immutable object before its constructor is finished

2)根据JLS的给定示例(2)和结论(3),我们可以安全地共享对 之前 的构造函数完成之前,即所有字段 final

2) According to JLS's given example (2) and conclusion (3) it seems, that we can safely share reference to immutable object before its constructor is finished, i.e. when all its fields are final.

是否有一些矛盾?

:我的意思。如果我们以这样的方式修改类,那么 y 也会 final (2):

EDIT-1: What I exactly mean. If we will modify class in example such way, that field y will be also final (2):

class FinalFieldExample { 
    final int x; 
    final int y; 
    ...

因此在 reader c $ c>方法将保证:

hence in reader() method it will be guaranteed, that:

if (f != null) { 
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4, isn't it???

如果是,为什么我们应该避免写对象 f f 的所有字段都是final?

If so, why we should avoid writing reference to object f before it's constructor is finished (according to (1)), when all fields of f are final?

推荐答案

我认为这些是略有不同的问题,是不矛盾的。

I believe these are slightly different issues that are not contradictory.

JLS引用要存储一个对象引用在其他线程可以在构造函数完成之前看到它的地方。例如,在构造函数中,你不应该将一个对象放入其他线程使用的 static 字段中,也不应该分叉线程。

The JLS reference is taking about storing an object reference in a place where other threads can see it before the constructor is finished. For example, in a constructor, you should not put an object into a static field that is used by other threads nor should you fork a thread.

  public class FinalFieldExample {
      public FinalFieldExample() {
         ...
         // very bad idea because the constructor may not have finished
         FinalFieldExample.f = this;
         ...
      }
  }

在一个construtor中启动线程:

You shouldn't start the thread in a construtor either:

  // obviously we should implement Runnable here
  public class MyThread extends Thread {
      public MyThread() {
         ...
         // very bad idea because the constructor may not have finished
         this.start();
      }
  }

即使所有字段 final 在类中,在构造函数完成之前将对象的引用共享给另一个线程不能保证在其他线程开始使用该对象的时候字段已经设置。

Even if all of your fields are final in a class, sharing the reference to the object to another thread before the constructor finishes cannot guarantee that the fields have been set by the time the other threads start using the object.

我的答案是在构造函数完成后使用一个没有同步的对象。这是一个略有不同的问题,虽然类似于关于构造函数,缺乏同步和编译器的操作重新排序。

My answer was talking about using an object without synchronization after the constructor had finished. It's a slightly different question although similar with regards to constructors, lack of synchronization, and reordering of operations by the compiler.

在JLS 17.5-1他们< t 在构造函数内部分配静态字段。他们在另一个静态方法中分配静态字段:

In JLS 17.5-1 they don't assign a static field inside of the constructor. They assign the static field in another static method:

static void writer() {
    f = new FinalFieldExample();
}

这是关键的区别。

这篇关于在对象的构造函数完成之前关于对象的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆