之前我有一個關於如何使對象保證爲Java存儲模型爲線程安全的問題。在構造函數中進行同步以使其發生 - 在
我讀過很多說,寫一個同步範圍在構造函數中沒有意義,但爲什麼不呢?是的,只要構造中的對象不在線程之間共享(不應該是),除了構造線程之外的任何線程都可以達到任何同步(this){...},所以在那裏不需要在構造函數中創建該範圍以排除它們。但同步的範圍不僅僅是排除在外;他們也被用來創造發生之前的關係。 JLS.17.4
這裏是一個示例代碼,使我的觀點清晰。
public class Counter{
private int count;
public Counter(int init_value){
//synchronized(this){
this.count = init_value;
//}
}
public synchronized int getValue(){
return count;
}
public synchronized void addValue(){
count++;
}
}
想想線程t0創建一個Counter對象,另一個線程t1使用它的情況。如果在構造函數中有同步語句,它顯然會保證是線程安全的。 (因爲同步作用域中的所有操作之間都有一個「先發生之前」關係)。但是,如果不是,即沒有同步化語句,那麼Java存儲器模型仍然保證可以通過t1看到count的t0初始化寫操作?我想不是。這就像f.y在JLS.17.5的示例代碼17.5-1中可以看到0一樣。與JSL.17.5-1的情況不同,現在第二個線程僅從同步方法訪問字段,但我認爲同步語句在這種情況下無法保證有效。 (他們不會在t0之前創建任何發生在任何動作之前的關係)。有人說,關於一個發生的規則 - 在構造函數結束之前發生的邊緣保證了它,但規則似乎只是說構造函數發生 - finalize()之前。
然後,我應該在構造函數中寫入synchronized語句以使對象線程安全嗎?或者是否存在關於我錯過的Java內存模型的一些規則或邏輯,實際上並不需要這些?如果我是真的,甚至openjdk的Hashtable(雖然我知道它已經過時)似乎不是線程安全的。
還是我錯了線程安全的定義和關於併發策略?如果我通過線程安全的方式將Counter對象從t0傳送到t1,例如通過一個易變的變量,似乎沒有問題。 (在這種情況下,t0的構造發生在易失性寫入之前,發生在t1發生易失性讀取之前,在t1發生之前發生)。我應該始終傳輸線程安全對象(但不是不可變的)之間的線程通過一種方式,導致發生之前的關係?
「考慮一個線程t0創建一個Counter對象,另一個線程t1使用它的情況。」在構造函數返回t0之前,t1如何引用'Counter'? – bradimus
您的問題已通過安全發佈解決。 – assylias