我目前正在阅读Java Concurrency in Practice by Brian Goetz。在第51页。在其中一个脚注中,他说:
虽然看起来构造函数中设置的字段值是第一个 写入这些字段的值,因此没有“较旧” 值被视为过时值,Object构造函数首先写入 子类构造函数运行之前的所有字段的默认值。它是 因此可以将字段的默认值视为陈旧 值。
所以,最终字段的概念现在还不清楚。考虑样本类:
public class MyClass{
private final MyImmutableClass mic;
public MyClass(){
mic = MyImmutableClass.empty();
}
}
根据上面的脚注,mic
字段 分配了两次 ,一次由Object
的构造函数和一次由MyClass
的构造函数本身。现在,假设我们不安全地发布了MyClass
个对象(例如通过public
字段):
public final MyClass mc;
谁保证在一致状态下的任何线程始终都会观察到mc
?为什么某个线程不会意外地遵守默认值?
据我所知,final
字段本身只保证在构造对象后不能分配引用。如果我们声明mc
不稳定,那就很清楚了。读取该字段的任何线程都应该直接从内存中读取它。禁止从缓存中读取它。
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
字段的默认值吗?
答案 0 :(得分:5)
mc
是一个实例变量。这意味着您必须拥有包含mc
的类的完全初始化实例,以便访问某个实例的mc
的任何代码都不会抛出NullPointerException
。因此,mc
肯定会在访问时初始化。
答案 1 :(得分:1)
... Object构造函数首先将默认值写入all 子类构造函数运行之前的字段...
Object
类构造函数无法查看仅属于MyClass
的{{1}}成员(不是从MyClass
继承而来的)。因此,上述语句是正确的,Object
类无法实例化成员变量Object
。
...根据上面的脚注,麦克风领域分配了两次, 一次是Object的构造函数,一次是MyClass的构造函数 本身...
没有。 mic
构造函数仅初始化它的成员变量。之后Object
构造函数会将其初始化为MyClass
。最后,您将拥有mic
实例。因此MyClass
未被分配两次,即使mic
是非最终的。
发布示例:代码段未完成。但是,跨多个线程访问某些东西取决于许多事情,例如它是否是静态成员?, parent在某处被引用为静态成员?, where和当mic
被初始化时?(默认值mc
肯定是通过构造函数)。 如果不是静态成员,则应该是单身等......