code

2016年12月2日 星期五

Java concurrency 2 - Locks

thread-safe variable不見得構成一個thread-safe class

如果我們要用更多的state在我們的class的話,有比較好的方法嗎? 例如以下:

@NotThreadSafepublic class UnsafeCachingFactorizer implements Servlet {
    private final AtomicReference<BigInteger> lastNumber           = new AtomicReference<BigInteger>();

    private final AtomicReference<BigInteger[]> lastFactors            = new AtomicReference<BigInteger[]>();

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        if (i.equals(lastNumber.get()))
            encodeIntoResponse(resp, lastFactors.get() );
        else {
            BigInteger[] factors = factor(i);
            lastNumber.set(i);
            lastFactors.set(factors);
            encodeIntoResponse(resp, factors);
        }
    }
}

我們想要也採用thread-safe object type來當作我們的data member type,不過不幸的是,
以上class並非thread-safe! 上面的問題在於,雖然兩個變數都用了AtomicReference來想要達成
thread-safe,但是lastNumer事實上是要等於lastFactors的乘積(後者為前者的因數),所以在service 
method中,兩者在update的時候,並非一個完整的atomic operation,所以在multithreading的時候,
會造成race condition。

我們需要一個機制來將某些程式碼,組合成一個atomic operation。

Java內建的Lock (intrinsic lock / monitor / mutex)

Java提供了一個lock機制,利用synchronized這個關鍵字,可以保衛一段被{ } 框住的程式碼,確保
裡面的所有指令構成一個atomic operation。

所謂的lock,其實就是某個object reference,例如一個宣告synchroized的method,則保衛這個method
裡面所有指令的lock就是這個method所屬的instance:

synchronized void test() {
    //以下所有的程式碼都視為一個atomic operation    //lock為此class instance}

public static synchronized void staticTest() {
    //以下所有的程式碼都視為一個atomic operation    //lock為此class object}

public void test2() {
    synchronized (this) {
      //lockthis
} }

這個lock只能一次被一個thread佔有,所以其他的thread要執行被lock保衛的block的時候,只能等待。
lock在一個thread開始進入block時獲得,在離開或丟出exception後釋放。

我們可以改寫 UnsafeCachingFactorizer 裡面的service method,使用intrinsic lock來保護這個method:\

@ThreadSafepublic class SynchronizedFactorizer implements Servlet {
    @GuardedBy("this") private BigInteger lastNumber;
    @GuardedBy("this") private BigInteger[] lastFactors;
    public synchronized void service(ServletRequest req,
                                     ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        if (i.equals(lastNumber))
            encodeIntoResponse(resp, lastFactors);
        else {
            BigInteger[] factors = factor(i);
            lastNumber = i;
            lastFactors = factors;
            encodeIntoResponse(resp, factors);
        }
    }
}

不過這其實就讓這個class變成non-multithreading class! 因為所有的thread都要排隊等待結果,等於
single thread的效能,白寫了!

沒有留言:

張貼留言