利用Callable,Future以及FutureTask实现一个缓存

一,利用Callable,Future以及FutureTask实现一个缓存

利用Callable,Future以及FutureTask实现一个缓存实现一个多线程环境下的缓存
一步一步进行设计分析优化

二,实现
  • 1
interface Computable{
    V compute(K key) throws InterruptedException;
}

class Memoizer {
    private final Map cache = new HashMap();
    Computable computation;
    Memoizer(Computable computation){
        this.computation = computation;
    }
    public synchronized V get(K key) throws InterruptedException {
              //从缓存中取结果. 如果结果存在,则直接返回; 若结果不存在,则进行计算
        V result = cache.get(key);
        if(result == null){
            result = computation.compute(key);
            cache.put(key,result);
        }
        return result;
    }
}

上面的代码实现存在一个问题-----伸缩性问题. 由于get方法被synchronized关键字修饰,每次只能有一个线程能够取结果.如果当一个线程结果不存在,需要计算执行的时候,而这个任务执行时间很长,那么其他线程就需要等待很长时间.
我们可以使用线程安全的ConcurrentHashMap进行替换HashMap,且去掉get方法的synchronized关键字

  • 2
interface Computable{
    V compute(K key) throws InterruptedException;
}

class Memoizer {
    private final Map cache = new ConcurrentHashMap();
    Computable computation;
    Memoizer(Computable computation){
        this.computation = computation;
    }
    public V get(K key) throws InterruptedException {
        //此处由ConcurrentHashMap来保证线程安全
        V result = cache.get(key);
        //以下代码可以并发执行
        if(result == null){
            result = computation.compute(key);
            cache.put(key,result);
        }
        return result;
    }
}

这种方案解决了伸缩性问题,吞吐量得到了提高.
但是存在一个重复计算的问题. 假设一个任务需要执行很长时间,当第一次通过get取结果的时候, 结果为空,需要执行,且是长时间的执行,那么这时候第二线程也过来取同样key的结果,第一次进行运算的结果(需要长时间运行)还没有出来,那么第二次得到的结果也是空(第二个线程不知道其他线程正在进行计算),那么再次进行了重复的运算.
在这里我们考虑使用FutureTask进行优化,利用他的FutureTask的get方法.

  • 3
interface Computable{
    V compute(K key) throws InterruptedException;
}

class Memoizer {
    private final Map> cache = new ConcurrentHashMap>();
    Computable computation;
    Memoizer(Computable computation){
        this.computation = computation;
    }
    public V get(K key) throws InterruptedException {
        //此处由ConcurrentHashMap来保证线程安全
        Future result = cache.get(key);
        
        if(result == null){
            Callable task = new Callable(){
                @Override
                public V call() throws Exception {
                    return computation.compute(key);
                }
            };
            
            FutureTask futureTask = new FutureTask(task);
            //缓存不存在则加入
            result = cache.putIfAbsent(key, futureTask);
            if(result == null){
                result = futureTask;
                futureTask.run();
            }
            
            try {
                //等待运算结果返回
                return result.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
                //遇到任务的终止,或者其他异常则移除缓存
                cache.remove(key,result);
            }
        }
        return null;
        
    }
}

这个缓存实现存在一个问题-----缓存逾期的问题

参考:
<>

你可能感兴趣的:(利用Callable,Future以及FutureTask实现一个缓存)