LongAdder小优化

众所周知,LongAdder通过分段更新的方式,保证了在高并发下依然性能很好。Long值在其内部是分段保存的,只有在真正获取Longadder的值的时候才会去计算。

  public long sum() {
        Cell[] as = cells; Cell a;
        long sum = base;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

在我的使用过程中,经常会碰到的场景是,对LongAdder写操作的并发很高,但是对值的读并发同样很高,这时候,每次获取值都要调用sum()去实时计算就会显得比较累赘,影响性能。所以在这个实现已经很完美的类上,做了一个小小的优化,思路很简单,缓存上次计算的long值,除非有写操作,否则直接读取缓存的值即可。
具体见代码&注释

public class CacheLongAdder extends LongAdder implements Serializable {

    private static final long serialVersionUID = 7249069246863181097L;

    /**
     * 上次写时间
     */
    private volatile Long lastModify = null;
    /**
     * 上次读时间
     */
    private volatile Long lastRead = null;

    /**
     * 缓存的long值
     */
    private Long cacheValue = null;

    public CacheLongAdder() {
        this.lastModify = System.currentTimeMillis();
    }

    @Override
    public void add(long x) {
        super.add(x);
        // 不需要对此值的变更进行并发控制,判断缓存值失效只需要比较大小即可
        lastModify = System.currentTimeMillis();
    }

    @Override
    public long sum() {
        if (cacheValue == null || lastRead == null || lastRead < lastModify) {
            cacheValue = super.sum();
            // 在没有写操作的情况下,直接使用缓存long值即可
            // 只有在缓存失效,即发生了新的写操作的情况下,才需要更新lastRead的时间,避免下次读操作的判断错误
            // 对并发情况下可能存在的误差是可以容忍度的,因为LongAdder本身的sum方法就不是原子性的
            lastRead = System.currentTimeMillis();
        }
        return cacheValue;
    }
}

你可能感兴趣的:(LongAdder小优化)