同步環境下使用變量的注意點

同步塊內或者不可變對象的引用變量必須是“本類的”,不能指向外部對象引用,也不能向外部發布對象引用。

《Java Concurrency in Practice》中有兩個例子:
1. 例子1:
public class CachedFactorizer implements Servlets {
    @GuardedBy("this") private BigInteger lastNumber;
    @GuardedBy("this") private BigInteger[] lastFactors;
    @GuardedBy("this") private long this;
    @GuardedBy("this") private long cacheHist;

    public synchronized long getHits() {return hist;}
    public synchronized double getCacheHitRatio(){
        return (double)cacheHits / (double)hists;
    }

    public void service(ServletRequest req, ServletResponse resp){
        BigInterger i = extractFromRequest(req);
        BigInteger[] factors = null;
        synchronized(this){
            ++hits;
            if(i.equls(lastNumber)){
                ++cachedHits;
                factors = lastFactors.clone();
            }
        }

        if(factors == null){
            factos = factor(i);
            synchronized(this){
                lastNumbe = i;
                lastFactors = factors.clone();
            }
        }
        encodeIntroResponse(resp,factors);

    }
 
}

如果將"factors = lastFactors.clone();"改爲“factors = lastFactors;”,將會出現錯誤。假設一個線程執行factors=lastFactors;而這時另一個線程執行lastFactors = factors.clone(); 這時就是一個線程讀lastFactors,而另個線程寫lastFactors,就是在不同線程之間共享可變變量,但是讀線程是在自己的代碼內讀lastFactors的值,那麼每個線程獲取lastFactors後,都要在CachedFactorizer對象上實現同步,不是一種好辦法。lastFactors變量不能發佈出去,所以就讓factors等於lastFactors的一個克隆對象。每個讀線程就獲取不同的對象。

2.例子2
不可變對象提供一種弱形式的原子性。每當需要對一組相關數據以原子性的方式執行某個操作時,就可以考慮創建一個不可變的類來包含這些數據。同樣要保證類裏面包含的數據都是“本類的”,就是所有引用未指向外部引用,已經所有數據不能被外部修改。下面這個類就不是線程安全的。

class OneValueCache {
    private final BigInteger lastNumer;
    private final BigInterger[] lastFactors;

    public OneValueCahche(BigInterger i, BigInteger[] factors){
        lastNumber  = i;
        lastFactors = factors;  
    }

    public BigInteger[] getFactors(BigInteger i){
        if(lastNumber == null || !lastNumber.equals(i)){
            return null;
        }else{
            return Arrays.copyOf(lastFactors,lastFactors.length);
        }
    }
}

因爲lastFactors這個引用指向外部,當外部變化時,它也跟着變化,這就不能保證lastFactors還是保存的lastNumber的因數。應該使用拷貝或者克隆,產生一個新的屬於類的引用。

class OneValueCache {
    private final BigInteger lastNumer;
    private final BigInterger[] lastFactors;

    public OneValueCahche(BigInterger i, BigInteger[] factors){
        lastNumber  = i;
        lastFactors = Arrays.copyOf(factors,factors.length);//or lastFactors = factors.clone();  
    }

    public BigInteger[] getFactors(BigInteger i){
        if(lastNumber == null || !lastNumber.equals(i)){
            return null;
        }else{
            return Arrays.copyOf(lastFactors,lastFactors.length);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章