zoukankan      html  css  js  c++  java
  • 并发案例(一)


     
    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);
       }
    }
     
     
     
     
  • 相关阅读:
    123
    搭建https://github.com/vulhub/vulhub靶场_初稿
    【译】 渗透测试实战第三版(红队版)
    sqlmap
    fiddler一些高级用法
    安装django和selenium
    python中os模块
    python通过日志分析加入黑名单
    python简单的购物系统
    python文件操作
  • 原文地址:https://www.cnblogs.com/lmxxlm-123/p/11131914.html
Copyright © 2011-2022 走看看