鍍金池/ 教程/ Java/ 線程安全及不可變性
Slipped Conditions
阻塞隊列
無阻塞算法
嵌套管程鎖死
Java 并發(fā)性和多線程介紹
死鎖
線程安全及不可變性
并發(fā)編程模型
Java 中的讀/寫鎖
剖析同步器
競態(tài)條件與臨界區(qū)
多線程的優(yōu)點
CAS
線程通信
如何創(chuàng)建并運行 java 線程
阿姆達爾定律
避免死鎖
信號量
多線程的代價
饑餓和公平
線程池
重入鎖死
Java 中的鎖
Java 內存模型
線程安全與共享資源
Java 同步塊

線程安全及不可變性

當多個線程同時訪問同一個資源,并且其中的一個或者多個線程對這個資源進行了寫操作,才會產生競態(tài)條件。多個線程同時讀同一個資源不會產生競態(tài)條件。

我們可以通過創(chuàng)建不可變的共享對象來保證對象在線程間共享時不會被修改,從而實現線程安全。如下示例:

public class ImmutableValue{
    private int value = 0;

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

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

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

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

如果你需要對 ImmutableValue 類的實例進行操作,可以通過得到 value 變量后創(chuàng)建一個新的實例來實現,下面是一個對 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()方法都聲明為同步方法即可。

上一篇:嵌套管程鎖死下一篇:Slipped Conditions