Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.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_Final - Fatal编程技术网

Java 为什么发布最终字段是安全的?

Java 为什么发布最终字段是安全的?,java,multithreading,final,Java,Multithreading,Final,我现在正在读书。第51页。在其中一个脚注中,他说: 虽然在构造函数中设置的字段值似乎是第一个 写入这些字段的值,因此不存在“旧的” 要将值视为过时值,对象构造函数首先写入 在运行子类构造函数之前,所有字段的默认值。它是 因此,可以将字段的默认值视为过时值 价值观 所以,我现在还不清楚最终字段的概念。考虑样本类: public class MyClass{ private final MyImmutableClass mic; public MyClass(){

我现在正在读书。第51页。在其中一个脚注中,他说:

虽然在构造函数中设置的字段值似乎是第一个 写入这些字段的值,因此不存在“旧的” 要将值视为过时值,对象构造函数首先写入 在运行子类构造函数之前,所有字段的默认值。它是 因此,可以将字段的默认值视为过时值 价值观

所以,我现在还不清楚最终字段的概念。考虑样本类:

public class MyClass{
    private final MyImmutableClass mic;

    public MyClass(){
        mic = MyImmutableClass.empty();
    }
}
根据上述脚注,
mic
字段被赋值两次,一次由
对象的构造函数赋值,一次由
MyClass的构造函数本身赋值。现在,假设我们不安全地发布了一个
MyClass
对象(例如,通过
public
字段):

谁能保证任何线程始终在一致状态下观察到
mc
?为什么某些线程不能意外地观察到默认值

据我所知,
final
字段本身只能保证在对象构造之后不能分配引用。如果我们宣布mc为volatile,那就很清楚了。任何读取该字段的线程都应该直接从内存中读取它。禁止从缓存中读取

UPD:出版物示例:

public static void main(String[] args){
    class MyRunnable implements Runnable(){
        private SomeClass sc;
        public MyRunnable(SomeClass sc){
            this.sc = sc;
        }
        public void run(){
            //do some with sc
        }
    }
    SomeClass sc = getInitialized();
    ExecutorService es = Executors.newFixedThreadPool(10);
    MyRunnable mr = new MyRunnable(sc);
    //submiting mr to es 10 times
    es.awaitTemination();
    es.shutdown();
}

private static SomeClass getInitialized(){
    SomeClass sc = new SomeClass();
    sc. initialize();
    return sc;
}
public class SomeClass
    public MyClass mc;

    public void initialize(){
        mc = new MyClass();
    }
}

一个
SomeClass
实例将跨多个线程发布。某些线程是否可以观察
mic
字段的默认值?

mc
在您的示例中是一个实例变量。这意味着您必须拥有一个包含
mc
的类的完全初始化实例,以便访问某个实例的
mc
的任何代码都不会抛出
NullPointerException
。因此,
mc
在被访问时肯定会被初始化

…对象构造函数首先将默认值写入所有 子类构造函数运行之前的字段

对象
类构造函数无法看到只属于
MyClass
MyClass
成员(不是从
对象
继承的)。因此,上述语句是正确的,
Object
类无法实例化成员变量
mic

…根据上述脚注,麦克风场分配两次, 一次由对象的构造函数执行,一次由MyClass的构造函数执行 本身

否。
对象
构造函数仅初始化其成员变量。然后
MyClass
构造函数将初始化它的
mic
。最后,您将拥有
MyClass
实例。因此,
mic
不会被分配两次,即使
mic
不是最终的


发布示例:代码段未完成。但是,跨多个线程访问某个对象取决于许多因素,例如它是否是静态成员?、父对象是否在某处被引用为静态成员?、mc在何时何地初始化?(默认值
null
通过构造函数确定)。如果不是静态成员,它应该是单例,等等。

所以,如果我们以前没有访问过变量,就没有线程本地缓存,所以我们必须直接从内存中读取它。正确吗?@St.Antario我不确定我是否理解你的评论。在您的示例中,哪个代码将访问
mc
?你能展示一些我可以评论的代码片段吗?我添加了发布的示例。@St.Antario你没有在新代码片段中显示调用
initialize
,我真的不明白你的意思。他说,
对象的构造函数将默认值写入所有字段……他说的是正确的。你对super()构造函数的调用感到困惑……啊,你的意思是,只要调用super()就可以让派生类的所有字段都保留默认值,对吗?从技术上讲,Brian Goetz说“对象构造函数首先将默认值写入所有字段”是错误的。描述如何创建新实例,以及在调用任何构造函数(包括超类
java.lang.object
的构造函数)之前,“新对象中的所有实例变量,包括在超类中声明的实例变量,均初始化为其默认值(§4.12.5)”。然后,这没有什么区别,因为在编写默认值之后,接下来要调用的就是
java.lang.Object
constructor@ErwinBolwidt评论不错,很有意思。多谢!!我应该对此加以限定。虽然12.5并不意味着一个先发生后发生的关系,但在任何情况下,由于17.4.4,“将默认值(零、假或空)写入每个变量都与每个线程中的第一个操作同步。”并且由于构造函数是在线程上调用的,而x与y同步意味着x发生在y之前,这也意味着在默认变量的赋值和java.lang.Object超类构造函数的调用之间存在一种先发生后发生的关系。我花了一些时间阅读java规范,现在我相信它也将首先初始化为默认值,然后执行初始化器。
public static void main(String[] args){
    class MyRunnable implements Runnable(){
        private SomeClass sc;
        public MyRunnable(SomeClass sc){
            this.sc = sc;
        }
        public void run(){
            //do some with sc
        }
    }
    SomeClass sc = getInitialized();
    ExecutorService es = Executors.newFixedThreadPool(10);
    MyRunnable mr = new MyRunnable(sc);
    //submiting mr to es 10 times
    es.awaitTemination();
    es.shutdown();
}

private static SomeClass getInitialized(){
    SomeClass sc = new SomeClass();
    sc. initialize();
    return sc;
}
public class SomeClass
    public MyClass mc;

    public void initialize(){
        mc = new MyClass();
    }
}