![](/img/trans.png)
[英]Does object construction guarantee in practice that all threads see non-final fields initialized?
[英]If you assign an Object to a final field, will other threads see previous updates of that Object's non-final/non-volatile fields?
閱讀Java語言規范,我發現了關於最終字段的摘錄:
final字段的用法模型很簡單:在該對象的構造函數中設置對象的最終字段; 並且在對象的構造函數完成之前,不要在另一個線程可以看到的地方寫入對正在構造的對象的引用。 如果遵循此原因,那么當另一個線程看到該對象時,該線程將始終看到該對象的最終字段的正確構造版本。 它還將看到那些最終字段引用的任何對象或數組的版本,這些字段至少與最終字段一樣是最新的 。
鏈接: https : //docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5
我的問題是,“版本”是否意味着更新? 這意味着最終字段引用的對象的非final / non-volatile字段也將在構造后從主內存(而不是本地緩存)中讀取?
例
所以我們假設thread #1
創建了一個objectB
並設置了一個非final / non-volatile字段。
然后thread #2
集,同一領域不同的東西,創造一些其他的objectA
設定為最后一個字段objectB
,然后把該objectA
的地方在那里thread #1
可以得到它。
thread #1
然后獲取objectA
,並將其最終字段視為objectB
。 thread #1
是否有可能看不到thread #2
對objectB
所做的更改?
或者這里有一些顯示我的意思的代碼:
public class Test {
private static final ConcurrentLinkedQueue<A> myAs = new ConcurrentLinkedQueue<>();
private static long timer = System.nanoTime() + 3000000000L; // 3 seconds into the future
public static void main(String... args) {
B myB = new B("thread #1"); // Set in thread 1
new Thread(() -> {
myB.setString("thread #2"); // Set in thread 2
myAs.add(new A(myB));
}).start();
for(long i = 0; i < x; i = System.nanoTime()) {} // Busy-wait for about 3 seconds
System.out.println(myAs.poll().getB().getString()); // Print out value
}
public static class A {
private final B b;
public A(B b) {
this.b = b;
}
public B getB() {
return b;
}
}
public static class B {
private String s = null;
public B(String s) {
this.s = s;
}
public String getString() {
return s;
}
public void setString(String s) {
this.s = s;
}
}
}
代碼似乎讀取了更新的值,但我不確定這是否僅僅是出於隨機運氣。
“ thread #1
是否有可能看不到thread #2
對objectB
所做的更改?”
是。 因為thread #1
可以緩存值。
來自spec的引用意味着happened before
最終字段值的分配和對象的發布happened before
了。
這很有趣,我想我實際上已經讀過它了...這是一個例子(如果我沒記錯的話):
static class Holder {
private final String[] names;
static Holder newHolder;
public Holder() {
super();
names = new String[3];
names[0] = "first";
names[1] = "last";
}
public void newObject() {
newHolder = new Holder();
newHolder.names[2] = "oneMore";
}
public void readObject() {
System.out.println(Arrays.toString(newHolder.names));
}
}
假設這里涉及兩個線程: ThreadA
和ThreadB
。 現在還假設ThreadA
調用newObject
; 當它完成時, ThreadB
調用readObject
。
絕對不能保證ThreadB
將first, last, oneMore
打印first, last, oneMore
打印first, last, oneMore
; 只保證first
和last
肯定會出現。
一旦你想到在final fields used inside constructor
中使用的final fields used inside constructor
情況下使用的MemoryBarriers
,這個btw是完全可以理解的。
在當前的實現下,這實際上是這樣的:
public Holder() {
super();
names = new String[3];
names[0] = "first";
names[1] = "last";
}
// [StoreStore]
// [LoadStore]
在構造函數的末尾插入兩個障礙,阻止其他讀取和存儲發生。
恕我直言,你將永遠看到更新的價值..這里發生了兩件事
由於我們有一個凍結動作,其他線程應該能夠看到myB的內容
更多細節: https : //shipilev.net/blog/2014/jmm-pragmatics/#_part_v_finals
https://www.ibm.com/developerworks/library/j-jtp03304/index.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.