java中创建高效、可靠的高速缓存(1)

缓存,就是把计算结果放入内存中,下次使用的时候,直接从缓存中获取,不用重新进行耗时的计算,以提高效率。
那我们如何创建缓存呢?首先我们编写以下代码:

public interface Computable<A, V> { 
        /**
         * 耗时的计算逻辑
         * @param arg
         * @return
         * @throws InterruptedException
         */
        V compute(A arg) throws InterruptedException;
}

Computable接口表示耗时的计算逻辑,传入参数A,计算得到结果V;我们需要把结果V放入缓存中,以便下次传入A是立刻得到V结果。

public class Memoizer1<A, V> implements Computable<A, V> {
    private final Map<A, V> cache = new HashMap<A, V>();
    private final Computable<A, V> c;
    public Memoizer1(Computable<A, V> c) {
        this.c = c;
    }
    public synchronized V compute(A arg) throws InterruptedException {
        V result = cache.get(arg);
        if(result == null){
            result = c.compute(arg);
            cache.put(arg, result);
        }
        return result;
    }

}

类Memoizer1实现了这样的缓存,因HashMap是线程不安全的,在执行方法compute时加锁,保证只有一个线程进入此方法。这样是弱并发的,导致用缓存还不如每次都计算来的快;显然是不合理的。
如此我们就有了类Memoizer2,用线程安全的ConcurrentHashMap取代HashMap,改进Memoizer1糟糕的并发行为。

public class Memorizer2<A, V> implements Computable<A, V>{
    private final Map<A, V> cache = new ConcurrentHashMap<A, V>();
    private final Computable<A, V> c;
    public Memorizer2(Computable<A, V> c) {
        this.c = c;
    }
    public V compute(A arg) throws InterruptedException {
        V result = cache.get(arg);
        if(result == null){
            result = c.compute(arg);
            cache.put(arg, result);
        }
        return result;
    }

}

这样就可以了吗? 让我们来假设这种情况, 当一个线程正在计算compute方法的时候,又有另外线程来获得计算结果,但是它们并不知道这个计算正在进行中,所有可能又会重复计算。
so,我们希望无论用什么方法,能够表现出“线程X正在计算”,这样另外的线程到达查找结果,能够判断出已经有线程在计算中, 等待计算结果,然后拿走结果。
谁能达到这样的效果呢? FutureTask。 对,FutureTask.get 方法会一直阻塞,等待结果计算出来,并返回。这样我们的Memorizr3就出来了

public class Memorizer3<A, V> implements Computable<A, V>{
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;
    public Memorizer3(Computable<A, V> c) {
        this.c = c;
    }
    public V compute(final A arg) throws InterruptedException {
        Future<V> f = cache.get(arg);
        if(f == null){
            FutureTask<V> ft = new FutureTask<V>(new Callable<V>() {
                public V call() throws Exception {
                    return c.compute(arg);
                }
            });
            f = ft;
            cache.put(arg, ft);
            ft.run();//调用c.compute在这里
        }

        try {
            return f.get();
        } catch (ExecutionException e) {
            throw new RuntimeException(e.getCause());
        }
    }

}

这样就完美了吗?
还存在唯一的一个缺陷!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值