美文网首页
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设置为一个新的引用时,其他线程就会立即看到新缓存的数据


相关文章

  • 可变对象使用copy修饰、不可变对象使用strong会发生什么

    使用copy修饰不可变对象的目的,是防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发生变化,引起不...

  • OC基础-copy & mutableCopy

    按方法区分 copy:产生不可变对象 mutableCopy:产生可变对象 按拷贝深度区分 浅拷贝:不产生新对象,...

  • 4 不可变对象

    当满足以下条件对象是不可变的 当对象状态创建后就不能被修改 对象的所有域都是final类型 对象是正常创建的(对象...

  • python中可变对象和不可变对象

    Python在heap中分配的对象分成两类:可变对象和不可变对象。 所谓可变对象是指,对象的内容可变,而不可变对象...

  • python可变和不可变对象

    python中有可变对象和不可变对象,可变对象:list,dict.不可变对象有:int,string,float...

  • Python浅拷贝 深拷贝

    内存泄漏太可怕。 Python 可变对象 & 不可变对象 在Python中,对象分为两种:可变对象和不可变对象。 ...

  • python可变对象和不可变对象

    Python在heap中分配的对象分为两类:可变对象和不可变对象。 可变对象:list,dict 不可变对象:in...

  • Python中的可变对象和不可变对象

    Python中的可变对象和不可变对象 什么是可变/不可变对象 不可变对象,该对象所指向的内存中的值不能被改变。当改...

  • Guava 集合类

    不可变集合类 为什么要使用不可变集合不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的;不...

  • 关于 String

    1. String 不可变 不可变对象对象在创建完成之后,其状态不能再被改变,则该对象即为不可变对象 对象不可变具...

网友评论

      本文标题:4 不可变对象

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