2011-08-22 88 views
6

它應該是所有領域,包括超級領域,有目的不可變的java類'final'爲了線程安全或足夠沒有修飾符方法嗎?最終字段和線程安全

假設我有一個非final字段的POJO,其中所有字段都是一些不可變類的類型。這個POJO有getters-setters,並且有一個構造函數來設置一些初始值。如果我通過敲除修飾符方法來擴展此POJO,從而使其不可變,擴展類將是線程安全的嗎?

+2

你是什麼意思*敲出修飾符方法*修飾符方法?拋出所有setter的異常?這會違反[Liskov替代原則](http://en.wikipedia.org/wiki/Liskov_substitution_principle)。但是,是的,這個類會**是線程安全的。 –

+0

是的,拋出一個運行時異常或用一個空的主體覆蓋它們可能有一些日誌記錄。我知道這是違反LSP的。 – pcjuzer

回答

11

爲了以線程安全的方式使用不帶final字段的有效不可變對象,需要在初始化後使對象對其他線程可用時使用安全發佈習慣用法之一,否則這些線程可以看到部分初始化的對象狀態(從Java Concurrency in Practice):

  • 初始化從靜態初始化的對象引用;
  • 將引用存儲到易失性字段或AtomicReference中;
  • 將引用存儲到正確構造的對象的最終字段中;或
  • 將對其的引用存儲到由鎖正確保護的字段中。

聲明你的不可變對象final發佈此限制的領域(即它保證,如果其他線程看到該對象的引用,他們也看到了完全初始化狀態,其final場)。但是,通常情況下,它並不能保證其他線程在發佈時能夠看到對象的引用,因此您可能仍然需要使用安全發佈來確保它。

請注意,如果你的對象實現了接口,您可以使用Collections.unmodifiableList()使用的方式,等:

class ImmutableFooWrapper implements IFoo { 
    private final IFoo delegate; // final provides safe publication automatically 

    public ImmutableFooWrapper(IFoo delegate) { 
     this.delegate = delegate; 
    } 
    ... 
} 

public IFoo immutableFoo(IFoo foo) { 
    return new ImmutableFooWrapper(foo); 
} 
+0

感謝您的詳細解答。我在考慮使用委託的同一個解決方案,但是我總是用擴展名返回解決方案,因爲我猜'超級'引用也是最終的。我錯過了什麼? – pcjuzer

+0

@pcjuzer:'super'與併發無關,所以在擴展的情況下,無論如何您都需要安全的發佈。 – axtavt

+0

所以,這意味着如果你聲明瞭一個字段最終,那麼如果它正在被一個線程更新,那麼其他線程將不會看到它,如果它看到,那麼它會看到更新的狀態? – AKS

-2

是的,這將是不可改變的,因此線程安全的,但只是只要字段私人的。

+2

爲什麼這些字段實際上必須是私人的? – xSNRG