java并发编程实战5.6 构建高效且可伸缩的结果缓存

计算接口:

/**
 * @author yaoqiang
 * @create 2020-03-26 20:38
 * @desc 模拟计算类型
 **/
public interface Computable<A,V> {
    V compute(A a) throws InterruptedException;
}

计算实现:

/**
 * @author yaoqiang
 * @create 2020-03-26 20:40
 * @desc 计算实例
 **/
public class ExpensiveFunction implements Computable<String, BigInteger> {

    @Override
    public BigInteger compute(String s) throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        return new BigInteger(s);
    }
}

初步缓存实现:  

直接给计算方法上面加锁,这样可以实现缓存的目的,但是计算变成了同步方法,只能由一个线程能够计算,容易造成大量阻塞,有的时候甚至比不上不用缓存,这种方式过于保守

/**
 * @author yaoqiang
 * @create 2020-03-26 20:42
 * @desc 记忆
 **/
public class Memorizer<A,V> implements Computable<A,V> {

    private final HashMap<A,V> cache = new HashMap<>();

    private final Computable<A,V> computable;

    public Memorizer(Computable<A, V> computable) {
        this.computable = computable;
    }

    @Override
    public synchronized V compute(A a) throws InterruptedException {
        V v = cache.get(a);
        if(v == null){
            V result = computable.compute(a);
            cache.put(a, result);
        }
        return v;
    }
}

初步升级:

使用ConcurrentHashMap,具有更好的并发性能,但是存在两个缺点

1. 如果计算需要花费大量时间,第一个线程正在计算,而第二个线程准备计算同样的值,但是发现缓存里面没有,于是又要重复计算

2. 并发下的问题,如果两个线程,同时返回null,同样重复计算,重复添加,造成迷惑的结果

/**
 * @author yaoqiang
 * @create 2020-03-26 20:42
 * @desc 记忆
 **/
public class Memorizer<A,V> implements Computable<A,V> {

    private final ConcurrentHashMap<A,V> cache = new ConcurrentHashMap<>();

    private final Computable<A,V> computable;

    public Memorizer(Computable<A, V> computable) {
        this.computable = computable;
    }

    @Override
    public V compute(A a) throws InterruptedException {
        V v = cache.get(a);
        if(v == null){
            V result = computable.compute(a);
            cache.put(a, result);
        }
        return v;
    }
}

解决问题1:

放入一个准备future,放入map对象,如果有重复,通过future来获取,

FutureTask有几种状态:等待运行,正在运行,运行完成,只要有状态了,就不会在出现,因为运算是按过长而产生的重复计算

/**
 * @author yaoqiang
 * @create 2020-03-26 20:42
 * @desc 记忆
 **/
public class Memorizer<A,V> implements Computable<A,V> {

    private final ConcurrentHashMap<A,Future<V>> cache = new ConcurrentHashMap<>();

    private final Computable<A,V> computable;

    public Memorizer(Computable<A, V> computable) {
        this.computable = computable;
    }

    @Override
    public V compute(A a) throws InterruptedException, ExecutionException {
        Future<V> v = cache.get(a);
        if(v == null){
            Callable<V> callable = ()-> computable.compute(a);
            FutureTask<V> futureTask = new FutureTask<>(callable);
            v = futureTask;
            cache.put(a,v);
            futureTask.run();
        }
        return v.get();

    }
}

解决问题2:

再次判断,使用

putIfAbsent 来判断是否需要运算,避免多线程导致的重复运算,清理很重要,这也式为了保证缓存的准确性
/**
 * @author yaoqiang
 * @create 2020-03-26 20:42
 * @desc 记忆
 **/
public class Memorizer<A,V> implements Computable<A,V> {

    private final ConcurrentHashMap<A,Future<V>> cache = new ConcurrentHashMap<>();

    private final Computable<A,V> computable;

    public Memorizer(Computable<A, V> computable) {
        this.computable = computable;
    }

    @Override
    public V compute(A a) throws InterruptedException {
        while (true) {
            Future<V> v = cache.get(a);
            if (v == null) {
                Callable<V> callable = () -> computable.compute(a);
                FutureTask<V> futureTask = new FutureTask<>(callable);
                v = cache.putIfAbsent(a, futureTask);
                if (v == null) {
                    v = futureTask;
                    futureTask.run();
                }
            }
            try {
                return v.get();
            } catch (CancellationException e) {
                cache.remove(a);
            } catch (ExecutionException e) {
                cache.remove(a);
            }
        }

    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值