10、Java併發性和多執行緒-執行緒安全與不可變性

easonjim發表於2017-06-15

以下內容轉自http://ifeve.com/thread-safety-and-immutability/

當多個執行緒同時訪問同一個資源,並且其中的一個或者多個執行緒對這個資源進行了寫操作,才會產生競態條件。多個執行緒同時讀同一個資源不會產生競態條件。

我們可以通過建立不可變的共享物件來保證物件線上程間共享時不會被修改,從而實現執行緒安全。如下示例:

public class ImmutableValue{
    private int value = 0;

    public ImmutableValue(int value){
        this.value = value;
    }

    public int getValue(){
        return this.value;
    }
}

請注意ImmutableValue類的成員變數value是通過建構函式賦值的,並且在類中沒有set方法。這意味著一旦ImmutableValue例項被建立,value變數就不能再被修改,這就是不可變性。但你可以通過getValue()方法讀取這個變數的值。

(譯者注:注意,“不變”(Immutable)和“只讀”(Read Only)是不同的。當一個變數是“只讀”時,變數的值不能直接改變,但是可以在其它變數發生改變的時候發生改變。比如,一個人的出生年月日是“不變”屬性,而一個人的年齡便是“只讀”屬性,但是不是“不變”屬性。隨著時間的變化,一個人的年齡會隨之發生變化,而一個人的出生年月日則不會變化。這就是“不變”和“只讀”的區別。(摘自《Java與模式》第34章))

如果你需要對ImmutableValue類的例項進行操作,可以通過得到value變數後建立一個新的例項來實現,下面是一個對value變數進行加法操作的示例:

public class ImmutableValue{
    private int value = 0;

    public ImmutableValue(int value){
        this.value = value;
    }

    public int getValue(){
        return this.value;
    }

    public ImmutableValue add(int valueToAdd){
        return new ImmutableValue(this.value + valueToAdd);
    }
}

請注意add()方法以加法操作的結果作為一個新的ImmutableValue類例項返回,而不是直接對它自己的value變數進行操作。

引用不是執行緒安全的!

重要的是要記住,即使一個物件是執行緒安全的不可變物件,指向這個物件的引用也可能不是執行緒安全的。看這個例子:

public void Calculator{
    private ImmutableValue currentValue = null;

    public ImmutableValue getValue(){
        return currentValue;
    }

    public void setValue(ImmutableValue newValue){
        this.currentValue = newValue;
    }

    public void add(int newValue){
        this.currentValue = this.currentValue.add(newValue);
    }
}

Calculator類持有一個指向ImmutableValue例項的引用。注意,通過setValue()方法和add()方法可能會改變這個引用。因此,即使Calculator類內部使用了一個不可變物件,但Calculator類本身還是可變的,因此Calculator類不是執行緒安全的。換句話說:ImmutableValue類是執行緒安全的,但使用它的類不是。當嘗試通過不可變性去獲得執行緒安全時,這點是需要牢記的。

要使Calculator類實現執行緒安全,將getValue()、setValue()和add()方法都宣告為同步方法即可(下一節講解)。

==>如有問題,請聯絡我:easonjim#163.com,或者下方發表評論。<==


相關文章