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);
}
}