美文网首页
4 不可变对象

4 不可变对象

作者: 史小猿 | 来源:发表于2018-09-19 16:38 被阅读10次

    当满足以下条件对象是不可变的

    • 当对象状态创建后就不能被修改
    • 对象的所有域都是final类型
    • 对象是正常创建的(对象创建期间,this没有逸出)
      不可变对象一定是线程安全的

    所有字段都是final修饰 是有final修饰不仅仅是从语义上说明被修饰字段的引用不可改变, 更重要的是这个语义在多线程环境下由Java内存模型保证被修饰字段所引用对象的初始化安全, 既final修饰字段在其他线程可见时 它必定是初始化完成的 相反 非final修饰字段由于缺少该保证 可能到一个线程看到一个字段的时候 其未被初始完成 从而导致一些可变预料的情况
    例子如图,如果变量没有被final修饰,没初始化完全的时候,就可能被其他线程使用

    /**
     * OneValueCache
     * <p/>
     * Immutable holder for caching a number and its factors
     *
     * @author Brian Goetz and Tim Peierls
     */
    @Immutable
    public class OneValueCache {
        private final BigInteger lastNumber;
        private final BigInteger[] lastFactors;
    
        public OneValueCache(BigInteger i,
                             BigInteger[] factors) {
            lastNumber = i;
            lastFactors = Arrays.copyOf(factors, factors.length);
        }
    
        public BigInteger[] getFactors(BigInteger i) {
            if (lastNumber == null || !lastNumber.equals(i))
                return null;
            else
                return Arrays.copyOf(lastFactors, lastFactors.length);
        }
    }
    

    用不可变对象解决因数分解缓存的问题

    /**
     * UnsafeCachingFactorizer
     *
     * Servlet that attempts to cache its last result without adequate atomicity
     *
     * @author Brian Goetz and Tim Peierls
     */
    
    @NotThreadSafe
    public class UnsafeCachingFactorizer extends GenericServlet 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);
            }
        }
    
        void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
        }
    
        BigInteger extractFromRequest(ServletRequest req) {
            return new BigInteger("7");
        }
    
        BigInteger[] factor(BigInteger i) {
            // Doesn't really factor
            return new BigInteger[]{i};
        }
    }
    

    加锁解决方案

     * SynchronizedFactorizer
     *
     * Servlet that caches last result, but with unnacceptably poor concurrency
     *
     * @author Brian Goetz and Tim Peierls
     */
    
    @ThreadSafe
    public class SynchronizedFactorizer extends GenericServlet 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);
            }
        }
    
        void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
        }
    
        BigInteger extractFromRequest(ServletRequest req) {
            return new BigInteger("7");
        }
    
        BigInteger[] factor(BigInteger i) {
            // Doesn't really factor
            return new BigInteger[] { i };
        }
    }
    

    加锁优化(剔除不需要加锁的逻辑)

     * CachedFactorizer
     * <p/>
     * Servlet that caches its last request and result
     *
     * @author Brian Goetz and Tim Peierls
     */
    @ThreadSafe
    public class CachedFactorizer extends GenericServlet 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);
        }
    
        void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
        }
    
        BigInteger extractFromRequest(ServletRequest req) {
            return new BigInteger("7");
        }
    
        BigInteger[] factor(BigInteger i) {
            // Doesn't really factor
            return new BigInteger[]{i};
        }
    }
    

    不可变对象+volitatile解决方案

     * VolatileCachedFactorizer
     * <p/>
     * Caching the last result using a volatile reference to an immutable holder object
     *
     * @author Brian Goetz and Tim Peierls
     */
    @ThreadSafe
    public class VolatileCachedFactorizer extends GenericServlet implements Servlet {
        private volatile OneValueCache cache = new OneValueCache(null, null);
    
        public void service(ServletRequest req, ServletResponse resp) {
            BigInteger i = extractFromRequest(req);
            BigInteger[] factors = cache.getFactors(i);
            if (factors == null) {
                factors = factor(i);
                cache = new OneValueCache(i, factors);
            }
            encodeIntoResponse(resp, factors);
        }
    
        void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {
        }
    
        BigInteger extractFromRequest(ServletRequest req) {
            return new BigInteger("7");
        }
    
        BigInteger[] factor(BigInteger i) {
            // Doesn't really factor
            return new BigInteger[]{i};
        }
    }
    

    当一个线程将volatile类型的cache设置为一个新的引用时,其他线程就会立即看到新缓存的数据


    相关文章

      网友评论

          本文标题:4 不可变对象

          本文链接:https://www.haomeiwen.com/subject/jtminftx.html