posts - 9, comments - 4, trackbacks - 0, articles - 21
有一个很耗时的运算需要缓存其计算结果,采用备忘录模式实现,如:
Java代码 
  1. public interface Computable<A, V> {  
  2.     V compute(A arg) throws InterruptedException;  
  3. }  
  4.   
  5. public class ExpensiveFunction  
  6.         implements Computable<String, BigInteger> {  
  7.     public BigInteger compute(String arg) {  
  8.         // 假设这里非常耗时...  
  9.         return new BigInteger(arg);  
  10.     }  
  11. }  


备忘录缓存方案一:
Java代码 
  1. public class Memoizer1<A, V> implements Computable<A, V> {  
  2.     @GuardedBy("this")  
  3.     private final Map<A, V> cache = new HashMap<A, V>();  
  4.     private final Computable<A, V> c;  
  5.   
  6.     public Memoizer1(Computable<A, V> c) {  
  7.         this.c = c;  
  8.     }  
  9.   
  10.     public synchronized V compute(A arg) throws InterruptedException {  
  11.         V result = cache.get(arg);  
  12.         if (result == null) {  
  13.             result = c.compute(arg);  
  14.             cache.put(arg, result);  
  15.         }  
  16.         return result;  
  17.     }  
  18. }  

这里备忘录的整个compute过程被同步,相当于我最原始的低效方案,


备忘录缓存方案二:
Java代码 
  1. public class Memoizer2<A, V> implements Computable<A, V> {  
  2.     private final Map<A, V> cache = new ConcurrentHashMap<A, V>();  
  3.     private final Computable<A, V> c;  
  4.   
  5.     public Memoizer2(Computable<A, V> c) { this.c = c; }  
  6.   
  7.     public V compute(A arg) throws InterruptedException {  
  8.         V result = cache.get(arg);  
  9.         if (result == null) {  
  10.             result = c.compute(arg);  
  11.             cache.put(arg, result);  
  12.         }  
  13.         return result;  
  14.     }  
  15. }  

将线程安全性委给cache,注:这个方案的cache用了ConcurrentHashMap,它是线程安全的。

备忘录缓存方案三:
Java代码 
  1. public class Memoizer3<A, V> implements Computable<A, V> {  
  2.     private final Map<A, Future<V>> cache  
  3.             = new ConcurrentHashMap<A, Future<V>>();  
  4.     private final Computable<A, V> c;  
  5.   
  6.     public Memoizer3(Computable<A, V> c) { this.c = c; }  
  7.   
  8.     public V compute(final A arg) throws InterruptedException {  
  9.         Future<V> f = cache.get(arg);  
  10.         if (f == null) {  
  11.             Callable<V> eval = new Callable<V>() {  
  12.                 public V call() throws InterruptedException {  
  13.                     return c.compute(arg);  
  14.                 }  
  15.             };  
  16.             FutureTask<V> ft = new FutureTask<V>(eval);  
  17.             f = ft;  
  18.             cache.put(arg, ft);  
  19.             ft.run(); // call to c.compute happens here  
  20.         }  
  21.         try {  
  22.             return f.get();  
  23.         } catch (ExecutionException e) {  
  24.             throw launderThrowable(e.getCause());  
  25.         }  
  26.     }  
  27. }  

使用Future(相当上一帖的Entry)封装,因为这里资源获取过程不固定(通用方案),所以使用了Callable进行回调资源获取过程(求值),
这个方案的 if (f == null) 不安全,特殊性可能会进行多次运算,下面的方案四解决这个问题。

备忘录缓存方案四(最终方案):
Java代码 
  1. public class Memoizer<A, V> implements Computable<A, V> {  
  2.     private final ConcurrentMap<A, Future<V>> cache  
  3.         = new ConcurrentHashMap<A, Future<V>>();  
  4.     private final Computable<A, V> c;  
  5.   
  6.     public Memoizer(Computable<A, V> c) { this.c = c; }  
  7.   
  8.     public V compute(final A arg) throws InterruptedException {  
  9.         while (true) {  
  10.             Future<V> f = cache.get(arg);  
  11.             if (f == null) {  
  12.                 Callable<V> eval = new Callable<V>() {  
  13.                     public V call() throws InterruptedException {  
  14.                         return c.compute(arg);  
  15.                     }  
  16.                 };  
  17.                 FutureTask<V> ft = new FutureTask<V>(eval);  
  18.                 f = cache.putIfAbsent(arg, ft);  
  19.                 if (f == null) { f = ft; ft.run(); }  
  20.             }  
  21.             try {  
  22.                 return f.get();  
  23.             } catch (CancellationException e) {  
  24.                 cache.remove(arg, f);  
  25.             } catch (ExecutionException e) {  
  26.                 throw launderThrowable(e.getCause());  
  27.             }  
  28.         }  
  29.     }  


只有注册用户登录后才能发表评论。


网站导航: