我使用joda,因爲它在多線程方面的聲譽很好。通過使所有Date/Time/DateTime對象不可變來實現多線程日期處理的效率很高。部分構造的對象/多線程
但是,我不確定喬達是否真的在做正確的事情。它可能會,但我很感興趣看到一個解釋。
當一個DateTime的一個toString()被調用喬達執行以下操作:
/* org.joda.time.base.AbstractInstant */
public String toString() {
return ISODateTimeFormat.dateTime().print(this);
}
所有格式化是線程安全的(他們永恆不變的爲好),但什麼是關於格式工廠:
private static DateTimeFormatter dt;
/* org.joda.time.format.ISODateTimeFormat */
public static DateTimeFormatter dateTime() {
if (dt == null) {
dt = new DateTimeFormatterBuilder()
.append(date())
.append(tTime())
.toFormatter();
}
return dt;
}
這是單線程應用程序中的一種常見模式,但已知它在多線程環境中容易出錯。
我看到以下的危險:空檢查期間
- 競爭條件 - >最壞的情況:獲得創建兩個對象。
沒有問題,因爲這只是一個輔助對象(與正常的單例模式不同),一個會保存在dt中,另一個會丟失,並且遲早會被垃圾回收。
- 的objec已完成之前初始化
靜態變量可能指向一個部分構造的對象(叫我瘋狂之前,閱讀有關本Wikipedia article類似的情況。)
所以Joda如何確保部分創建的格式化程序不會在此靜態變量中發佈?
感謝您的解釋!
雷託
+1。這是java併發模型中爲什麼不可變對象更「簡單」的另一個原因。但是,我不確定最後一段,請參閱JLS 17.4.4:「向每個變量寫入默認值(零,false或null) - 與每個線程中的第一個操作同步」。在讀取dt之前發生「寫入默認值」是必要的。 – irreputable 2010-03-24 20:45:43
是的,在讀取dt之前寫入默認值。這可以防止線程讀取「垃圾」值。但是在另一個線程中寫入字段(初始化dt字段)不與dt讀取具有hb關係。因此,我們可以隨時看到這兩個寫入中的任何一個(默認或來自初始化dt的線程)。我們必須讀取默認初始值的唯一時間是臨時性需求檢查。但是那時我們只提交一個默認值的讀取,並且在下一次迭代中說,該線程將一個對象寫入dt(僅用於該方法中的第一次讀取)。 – maxkar 2010-03-25 06:14:33