2017-02-15 18 views
1

根據我已閱讀的有關多線程的內容,如果您想創建一個由可變線程不安全類組成的不可變線程安全類,則需要將傳遞給構造函數的可變對象的防禦副本進行更改,以便在其中一些變化之後,您仍將原始版本保留在創建的對象中。例如:防禦性複製是否足以通過可變線程不安全的創建不可變的線程安全類?

import java.util.Date; 

public class Person { 
    private final String name; 
    private final Date birthDate; 
    ... 
    public Person(final String name, final Date birthDate, ...) { 
     this.name = name; 
     this.birthDate = new Date(birthDate.getTime()); 
     ... 
    } 
    ... 
} 

是不是有可能爲其他線程修改傳遞給構造函數的出生日期的構造函數被調用後,但由防守副本之前?

如果是,那麼調用構造函數的代碼是否需要確保在構造函數執行時不會修改傳遞的出生日期?

這是否違反了線程安全類的定義之一,即如果一個類在多線程環境中的行爲正確,而客戶端沒有任何額外的同步,則該類表示線程安全?

有沒有其他方法可以確保線程安全?

+1

爲什麼這不是線程安全的?只有一個線程會初始化該對象。 –

+0

您已經指出了確保線程安全的「複製對象」策略的一個限制。在這種情況下,提供所有其他解決方案來確保線程安全的答案太寬了 – ControlAltDel

+1

@AndrewJenkins Date參數不是不可變的 – ControlAltDel

回答

1

是的,在理論上,傳入的birthDate可以在對Person構造函數和防禦副本的調用之間改變。這會假設birthDate值不是線程安全的。通常情況下,如果你擔心這個問題,你可以在它周圍放一個同步塊,以確保不會發生這種瘋狂事件。由於該同步將圍繞Person構造函數延伸,因此您將得到很好的保護。

你只需要圍繞構造函數調用進行同步。一旦構建完成,就會存在一個防禦副本,而其他Person方法則是線程安全的。

線程安全可以通過多種方式實現 - 防禦性複製,同步,使用javax.concurrency包或者恰當的設計。這些都有不得不被理解的弱點。

1

是不是可能的構造函數被調用後,其他線程修改傳遞給構造函數的出生日期 但 之前提出的防禦副本?

是的,它的可能性。

如果是,那麼是不是就代碼調用構造函數需要確保 ,傳遞的出生日期不同時構造 執行修改?

是的,它應該如果要調用多個線程Person構造即代替共享創建的實例,您是使用不同的線程實例。在我看來,通常這很少見。

不違反一個線程安全類的定義之一, 一個,指出一類是線程安全的,如果它在多線程環境中正常的行爲 不上的任何額外 同步它的客戶?

我想,NO因爲線程安全類通常意味着你可以通過對構建正確實例的線程安全類多線程的也就是你會在某種主線程的創建實例首先,您將在許多奴隸/子/從屬線程中使用相同的實例來正確調用其業務方法。如果你不遵循這個設計,你可以自由地使用synchronization圍繞Person的構造函數,也就是說防禦性複製是不夠的。

線程安全通常意味着該類的任何業務方法都可以從各個線程中調用,並且它會產生正確的行爲,並假定該對象已經構建,這意味着您將不得不在這些業務方法中使用同步因爲它具有可變狀態,所以它是類。

防守副本只是釋放了從Date對象Person類人創建的,其中外Person類,這是所有的業務方法都需要正確同步自己。

如果有人對我說他的類是線程安全的,它通常意味着我作爲一個客戶端,我可以從多個線程調用該類的業務方法,但是我不會在多個線程中創建實例,即那裏將不會成爲線程之間的競爭條件,甚至無法創建單個共享實例。

併發的整點是單個共享實例

希望它有幫助!