缓存的简单实现方式

参考: 《Java 并发实践》 李大狗大神的大作和源码非常值得分析阅读

1.StringhashCode:数据缓存在类变量中

字符串类维护了一个常量池—每当使用String str="xxx"创建对象,都会首先检查字符串是否在常量池中—有的话直接返回池中对象的实例引用,否则则创建一个对象返回并将对象放进池中

没当调用 String.hashCode()方法时,字符串的hashCode便会缓存在String类变量中,下次查询是直接返回。

2.数据缓存在XXHashMap

HashMapConcurrentHashMap是最简单的缓存实现,必须使用缓存的项目初期可用XXHashMap实现。注意点有:

  1. 数据量不可太大,因为百万级别数据的扩容会耗费很长时间,可能造成后续数据积压。参考
  2. key获取value消耗时间太长的话,<K,V>V存放 FutrueTask<Value>
  3. k->v 的计算过程如果被取消或者中断,value存放的是无效的value, 此时需要移除无效的value,常用代码如下:
...
try{
    return feature.get();
}catch(Exception e){
    cache.remove(key);
}
...

综上,稍合理的缓存实现如下:

/**
 *计算接口
 */
public interface Computable<K,V>{
    V compute(A arg) throws InterruptedException;
}

/**
 *带有缓存实现的计算接口
 */
public class CachedComputer<K,V> implements Computable<K,V>{
    private final ComcurrentMap<K,Future<V>> cache = new ConcurrentHashMap<A,Future<V>>();
    
    private final Computable<K,V> c;
    
    public CachedComputer(Computable<K,V> c){
        this.c=c;
    }
    
    @Override
    public V compute(final K key) throws InterruptedException{
        while(true){
            Future<V> f=cache.get(key);
            if(f==null){
                Callable<V> eval=new Callable<V>(){
                    @Override
                    public V call() throws InterruptedException{
                        return c.compute(key);
                    }
                };
                FutureTask<V> ft=new FutureTask<V>(eval);
                f=cache.putIfAbsent(key,ft);
                if(f==null){
                    f=ft;
                    f.run();
                }
            }
            try{
                return f.get();
            }Cache(Exception e){
                cache.remove(key);
            }
        }
    }
    
}
3. 第二节的补充

第二节的实现仍然有几个问题亟ji2待解决:

  1. 缓存逾期问题—旧值逾期报废以为新值腾出空间;
  2. 缓存大小问题。

解决方法如下:

//逾期报废
value 包装成带有时间戳的对象(k,v,time),当put()或者get()时更新时间为System.currentTime();

启动线程检查逾期的value并移除,并且每次执行都sleep()一定的时间。

//缓存大小
使用LRU策略。

posted on 2018-10-04 15:19  coderDu  阅读(186)  评论(0编辑  收藏  举报