并发案例(一)


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

转载于:https://www.cnblogs.com/lmxxlm-123/p/11131914.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值