众所周知,如果我们有一些对象引用并且该引用具有最终字段 - 我们将看到来自最终字段的所有可访问字段(至少在构造函数完成时)
示例 1:
class Foo{
private final Map map;
Foo(){
map = new HashMap();
map.put(1,"object");
}
public void bar(){
System.out.println(map.get(1));
}
}
据我了解,在这种情况下,我们保证该bar()
方法始终输出object
,因为:
1. 我列出了类的完整代码,Foo
并且映射是最终的;
2. 如果某个线程会看到引用 ofFoo
并且这个引用 != null,那么我们可以保证从最终map
引用值到达的值是实际的。
我也认为
示例 2:
class Foo {
private final Map map;
private Map nonFinalMap;
Foo() {
nonFinalMap = new HashMap();
nonFinalMap.put(2, "ololo");
map = new HashMap();
map.put(1, "object");
}
public void bar() {
System.out.println(map.get(1));
}
public void bar2() {
System.out.println(nonFinalMap.get(2));
}
}
bar()
在这里,我们对方法有相同的保证,但是尽管分配发生在分配之前,我们仍然bar2
可以抛出 。NullPointerException
nonFinalMap
map
我想知道 volatile 怎么样:
示例 3:
class Foo{
private volatile Map map;
Foo(){
map = new HashMap();
map.put(1,"object");
}
public void bar(){
System.out.println(map.get(1));
}
}
据我了解,bar()
方法不能抛出NullPoinerException
,但可以打印null
;(我完全不确定这方面)
示例 4:
class Foo {
private volatile Map map;
private Map nonVolatileMap;
Foo() {
nonVolatileMap= new HashMap();
nonVolatileMap.put(2, "ololo");
map = new HashMap();
map.put(1, "object");
}
public void bar() {
System.out.println(map.get(1));
}
public void bar2() {
System.out.println(nonFinalMap.get(2));
}
}
我认为在这里我们对bar()
方法有相同的保证也bar2()
不能抛出NullPointerException
,因为nonVolatileMap
赋值写了更高的易失映射赋值,但它可以输出 null
在 Elliott Frisch 评论之后添加
通过比赛示例发布:
public class Main {
private static Foo foo;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
foo = new Foo();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (foo == null) ; // empty loop
foo.bar();
}
}).start();
}
}
请证明或更正我对代码片段的评论。