• 《JAVA并发编程实战》示例程序第一、二章


     第一章:简介

    程序清单1-1非线程安全的数值序列生成器

    import net.jcip.annotations.NotThreadSafe;
    
    @NotThreadSafe
    public class UnsafeSequence {
    	private int value;
    	
    	/*返回一个唯一的数值*/
    	public int getValue() {
    		return value++;		//三个操作:读取,加一,赋值。	多线程并发操作value可能导致步骤被打乱
    	}
    }
    

    程序清单1-2 线程安全的数值序列生成器

    public class Sequence {
    	private int value;
    	
    	/*返回一个唯一的数值*/
    	public synchronized int getValue() {	//各个线程串行访问
    		return value++;
    	}
    }
    

    第二章:线程安全性

    程序清单2-1 一个无状态的Servlet ,各个线程间没有共享状态

    @ThreadSafe
    public class StatelessFactorizer implements Servlet{
    	
    	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    		BigInteger i = extractFromRequest(req);
    		BigInteger[] factories = factor(i);
    		encodeIntoResponse(res,factories);
    	}
    }
    

      

    程序清单2-3 延迟初始化中的竞态条件(不要这么做)

    @NotThreadSafe
    public class LazyInitRace {
    	
    	private ExpensiveObject instance = null;	//竞态条件
    	
    	public ExpensiveObject getInstance() {
    		if(instance == null)
    			instance = new ExpensiveObject();
    		return new ExpensiveObject();
    	}
    }
    class ExpensiveObject{}
    

    程序清单2-4  使用AtomicLong类型的变量来统计已处理请求的数量

    /*servlet的状态就是计数器count的状态,count是线程安全的,所以servlet是线程安全的*/
    public class CountingFactorizer implements Servlet {
        private final AtomicLong count = new AtomicLong(0); //原子变量类,实现在数值和对象引用上的原子状态转换
        
        public long getCount() { return count.get(); }
    
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            BigInteger i = extractFromReqest(req);
            BigInteger[] factors = factor(i);
            count.incrementAndGet();
            encodeIntoResponse(res,factors);
        }
    }

    程序清单2-5 Servlet在没有足够原子性保证的情况下对其最近计算结果进行缓存(不要这么做)

    /**
     * 因数分解:
     * 在数学中,因数分解,又称素因数分解,是把一个正整数写成几个约数的乘积。例如,给出45这个数,它可以分解成3×3×5,根据算术基本定理,这样的分解结果应该是独一无二的
     * 
     * 
     * 希望提升Servlet性能,将最近的计算结果缓存起来,当2个连续的请求对相同的数值进行因数分解时,可以直接使用上一次的计算结果.
     * 
     * 原子引用本身是线程安全的,但是业务逻辑中存在竞态条件 : 
     *     lastFactors[0] * lastFactors[1] * .....  = lastNumber; 此条件不被破坏,Servlet才是线程安全的。
     */
    @NotThreadSafe
    public class UnsafeCachingFactorizer implements Servlet {
        //lastNumber、lastFactors本身是线程安全的
        private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>();        //上一次请求的 因数
        private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>();    //上一次请求的 因数分解结果
        
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            BigInteger i = extractFromReqest(req);
            if(i.equals(lastNumber)){
                encodeIntoResponse(res,lastFactors.get());
            } else {
                BigInteger[] factors = factor(i);
                lastNumber.set(i);                //无法保证同时与lastFactors更新,
                lastFactors.set(factors);        //无法保证同时与lastNumber更新
                encodeIntoResponse(res,factors);
            }
        }
    }

    程序清单2-6 Servlet能正确地缓存最新的最近计算结果,但并发性确非常糟糕(不要这么做)

    @ThreadSafe
    public class SynchronizedFactorizer implements Servlet {
        @GuardedBy("this") private  BigInteger lastNumber;        //上一次请求的 因数
        @GuardedBy("this") private BigInteger[] lastFactors;    //上一次请求的 因数分解结果
        
        public synchronized void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            BigInteger i = extractFromReqest(req);
            if(i.equals(lastNumber)){
                encodeIntoResponse(res,lastFactors);
            } else {
                BigInteger[] factors = factor(i);
                lastNumber = i;
                lastFactors = factors;
                encodeIntoResponse(res,factors);
            }
        }
    }

    程序清单2-7,如果内置所不可重置,那么以下代码将发生死锁

    /**
     * 一线程请求其它线程持有的锁时,发出请求的线程将会被阻塞。
     * 然而,内置锁是可重入的,某线程试图获得自己持有的锁时,这个请求将成功。
     * “重入” 意味着获取锁的粒度是“线程”,而不是“调用”
     * 
     * “重入”的实现:计数器。锁一次 +1,释放一次-1。 =0时锁被释放。
     * @author guchunchao
     *
     */
    public class Widget {
        public synchronized void widget() {
            //TODO ......
        }
    }
    
    class LoggingWidget extends Widget {
        @Override
        public synchronized void widget() {
            // TODO ......
            super.widget();    //如果内置锁不可重入,这里将无法获得Widget上的锁,因为这个锁已经被持有,而线程将永远停顿下去,等待一个永远也无法获得的锁。
        }
    }

    复合操作的原子性无法通过单个方法的synchronized保证

    //Vector类的每一个方法都是synchronized,仅保证单个方法的原子性;当多个方法组合在一起的复合操作时,不足以保证复合操作的原子性,需要额外加锁
    if(!vector.contains(name))
        vector.add(name);

      

    程序清单2-8  缓存最近执行因数分解的数值及其计算结果的Servlet

    //注意:在单个变量上实现原子操作来说,原子变量(Atomic.....)是很有用的,不要把原子变量和同步代码块同时使用,这会带来混乱,也不会再性能或安全性上带来任何好处。

    @ThreadSafe
    public class SynchronizedFactorizer {
          @GuardedBy("this") private  BigInteger lastNumber;        //上一次请求的 因数
          @GuardedBy("this") private BigInteger[] lastFactors;    //上一次请求的 因数分解结果
          @GuardedBy("this") private long hits;                    //请求数量
          @GuardedBy("this") private long cacheHist;                //缓存命中数量
          
          /**获取访Servlet调用问数量*/
          public synchronized long getHits() {return this.hits;}
          
          /**获取缓存命中率*/
          public synchronized double getHitsRatio() {return (double)cacheHist / (double)hits;}
          
          /**更细粒度的同步*/
          public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
              BigInteger i = extractFromReqest(req);                 //从页面获取待因数分解的数
              BigInteger[] factors = null;                        
              
              synchronized(this) {                                    
                  ++hits;                    //同步竞态条件:请求数 和 缓存命中数 可能同步更改
                  if(i.equals(lastNumber)){                        //命中缓存
                      ++cacheHist;        //同步竞态条件:请求数 和 缓存命中数 可能同步更改
                      factors = lastFactors.clone();
                    }
              }
              
              if(factors == null){        //缓存未命中,与上次请求的数值不一样
                  factors = factor(i);    //执行因数分解
                  synchronized(this) {
                      lastNumber = i;                    //同步缓存竞态条件    lastNumber = lastFactors[0] * lastFactors[1] * ......
                        lastFactors = factors.clone();    //同步缓存竞态条件,用clone是因为factors是在同步代码块儿外定义的变量    
                  }
                  encodeIntoResponse(res,factors);
              }
          }
    }

      

  • 相关阅读:
    jQuery如何获取选中单选按钮radio的值
    java计算出字符串中所有的数字求和?
    java 多线程对List中的数据进行操作
    MongoDB
    CentOS网卡一致性命名
    linux之list_for_each和list_for_each_entry函数
    linux开机启动项
    linux学习参考网站
    linux内核态获取纳秒ns时间
    Linux内核kfifo
  • 原文地址:https://www.cnblogs.com/guchunchao/p/10702509.html
Copyright © 2020-2023  润新知