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) {
//lock為this
}
}
這個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的效能,白寫了!
沒有留言:
張貼留言