1.非線程安全
懶加載 ,是非線程安全的。
當作一個線程剛非空判斷時,另外一個線程也進入非空判斷,則致使兩個線程都建立了一個ExpensiveObject對象。違背預期。
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null){
instance = new ExpensiveObject();
}
return instance;
}
}
2.線程安全
當沒有共享的狀態變量時,該類是線程安全。
@ThreadSafe
public class StatelessFactorizer implements Servlet {
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
3.非線程安全
因爲 ++ 操做符是非原子性操做的(要通過 讀取原數字 -> 增長值 -> 返回新值賦給count ).所以會有併發風險,須要進一步封裝處理
@NotThreadSafe
public class UnsafeCountingFactorizer implements Servlet {
private long count = 0;
public long getCount() { return count; }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
++count;
encodeIntoResponse(resp, factors);
}
}
4.線程安全
AtomicLong 是java提供的線程安全對象。
本質是把原始類型數據的非原子性操做封裝成原子性操做,以解決併發安全問題。
Atomic* 原子操做實現原理:
for循環中調用unsafe.compareAndSwapint,判斷當前值是否等於current。
若是不等於則繼續循環判斷;若是等於則賦值並返回增加後的值。
而unsafe是用final修飾的。
注意,在只有一個對象狀態變量時 ,是有效的。
@ThreadSafe
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp, factors);
}
}
5.線程安全,性能較差
本例將 service方法 進行了synchornized 處理 ,所以同一時間只有一個線程能夠持有該鎖。
當該線程退出該方法,會釋放鎖 ,其它線程才能夠得以進入。
這樣雖然解決了 線程安全問題 ,但若是service處理的操做比較複雜,致使一個線程長期持有該鎖,則性能問題暴露明顯。
@ThreadSafe
public 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);
}
}
}
6.線程不安全
判斷 lastNumber 的值 和 輸入值i ,是否相等後 ,若是有其餘線程 在這一瞬間作了 lastNumber.set()操做 ,則數據更新丟失;反之亦然。
注意本例,雖然使用了 final 來修飾變量 ,又使用了AtomicReference(java提供的線程安全對象),可是因爲是兩個變量,依然不是原子性操做,須要進一步封裝
@NotThreadSafe
public 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);
}
}
}
7.線程安全,併發接受量大(性能較好)
解決case 6 的問題,可使用 synchronized 修飾service方法,可是會有性能問題/併發接收量低 的問題,相似case 5.
所以採用 ,將 檢測操做 和 賦值操做 分離開 (同時也是將長持有 拆分爲短持有),而且都用內置鎖(synchronized),把代碼塊鎖住。能夠提高性能。
注意:
一共四個狀態變量。
hits,cacheHits 相關操做不是在synchronized方法中,就是在synchronized塊中。
lastNumber,lastFactors 在synchronized塊中。
i.equals(lastNumber) 和 lastNumber,lastFactors 操做 分離在兩個鎖塊中 —- 檢測與操做分離,保證線程安全。
@ThreadSafe
public class CachedFactorizer implements Servlet {
@GuardedBy("this") private BigInteger lastNumber;
@GuardedBy("this") private BigInteger[] lastFactors;
@GuardedBy("this") private long hits;
@GuardedBy("this") private long cacheHits;
public synchronized long getHits() { return hits; }
public synchronized double getCacheHitRatio() {
return (double) cacheHits / (double) hits;
}
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors == null) {
factors = factor(i);
synchronized (this) {
lastNumber = i;
lastFactors = factors.clone();
}
}
encodeIntoResponse(resp, factors);
}
}