ITPub博客

首页 > 应用开发 > Java > 构建高效且可伸缩的结果缓存

构建高效且可伸缩的结果缓存

Java 作者:壹頁書 时间:2015-08-25 15:22:00 0 删除 编辑
JAVA并发编程实践
第85页

环境 JDK1.6(貌似使用1.7编译会有问题)

第一版 synchronized 控制并发

  1. import java.math.BigInteger;
  2. import java.util.HashMap;
  3. import java.util.Map;

  4. interface Computable<A, V> {
  5.     V compute(A arg) throws InterruptedException;
  6. }

  7. class ExpensiveFunction implements Computable<String, BigInteger> {
  8.     @Override
  9.     public BigInteger compute(String arg) throws InterruptedException {
  10.         return new BigInteger(arg);
  11.     }
  12. }

  13. class Memoizer1<A, V> implements Computable<A, V> {
  14.     private final Map<A, V> cache = new HashMap<A, V>();
  15.     private final Computable<A, V> c;

  16.     public Memoizer1(Computable<A, V> c) {
  17.         this.c = c;
  18.     }

  19.     @Override
  20.     public synchronized V compute(A arg) throws InterruptedException {
  21.         V result = cache.get(arg);
  22.         if (result == null) {
  23.             result = c.compute(arg);
  24.             cache.put(arg, result);
  25.         }
  26.         return result;

  27.     }
  28. }
ExpensiveFunction 模拟一个很费时间的操作.比如数据库读取。

这个版本问题很明显,就是synchronized 限制了并发.



第二版 
ConcurrentHashMap替换HashMap

  1. import java.math.BigInteger;
  2. import java.util.Map;
  3. import java.util.concurrent.ConcurrentHashMap;

  4. interface Computable<A, V> {
  5.     V compute(A arg) throws InterruptedException;
  6. }

  7. class ExpensiveFunction implements Computable<String, BigInteger> {
  8.     @Override
  9.     public BigInteger compute(String arg) throws InterruptedException {
  10.         return new BigInteger(arg);
  11.     }
  12. }

  13. class Memoizer2<A, V> implements Computable<A, V> {
  14.     private final Map<A, V> cache = new ConcurrentHashMap<A, V>();
  15.     private final Computable<A, V> c;

  16.     public Memoizer2(Computable<A, V> c) {
  17.         this.c = c;
  18.     }

  19.     @Override
  20.     public V compute(A arg) throws InterruptedException {
  21.         V result = cache.get(arg);
  22.         if (result == null) {
  23.             result = c.compute(arg);
  24.             cache.put(arg, result);
  25.         }
  26.         return result;

  27.     }
  28. }
这个版本的问题是,在并发很高的情况下,一旦缓存失效,大量的请求会拥塞数据库的所有线程.造成数据库的间歇性问题.

第三版 Future

  1. import java.math.BigInteger;
  2. import java.util.Map;
  3. import java.util.concurrent.Callable;
  4. import java.util.concurrent.ConcurrentHashMap;
  5. import java.util.concurrent.ExecutionException;
  6. import java.util.concurrent.Future;
  7. import java.util.concurrent.FutureTask;

  8. interface Computable<A, V> {
  9.     V compute(final A arg) throws InterruptedException, ExecutionException;
  10. }

  11. class ExpensiveFunction implements Computable<String, BigInteger> {
  12.     @Override
  13.     public BigInteger compute(String arg) throws InterruptedException, ExecutionException {
  14.         return new BigInteger(arg);
  15.     }
  16. }

  17. class Memoizer3<A, V> implements Computable<A, V> {
  18.     private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
  19.     private final Computable<A, V> c;

  20.     public Memoizer3(Computable<A, V> c) {
  21.         this.c = c;
  22.     }

  23.     @Override
  24.     public V compute(final A arg) throws InterruptedException, ExecutionException {
  25.         Future<V> f = cache.get(arg);
  26.         if (f == null) {
  27.             Callable<V> eval = new Callable<V>() {

  28.                 @Override
  29.                 public V call() throws ExecutionException, InterruptedException {
  30.                     return c.compute(arg);
  31.                 }

  32.             };
  33.             FutureTask<V> ft = new FutureTask<V>(eval);
  34.             f = ft;
  35.             cache.put(arg, ft);
  36.             ft.run();
  37.         }
  38.         return f.get();
  39.     }
  40. }
这个版本的问题是
if(f==null)是非原子的"先检查后执行",可能会有多个线程同时进入该段代码.但是相对于第二版,概率已经大大降低.

最终版本

  1. import java.math.BigInteger;
  2. import java.util.Map;
  3. import java.util.concurrent.Callable;
  4. import java.util.concurrent.ConcurrentHashMap;
  5. import java.util.concurrent.ExecutionException;
  6. import java.util.concurrent.Future;
  7. import java.util.concurrent.FutureTask;

  8. interface Computable<A, V> {
  9.     V compute(final A arg) throws InterruptedException;
  10. }

  11. class ExpensiveFunction implements Computable<String, BigInteger> {
  12.     @Override
  13.     public BigInteger compute(String arg) throws InterruptedException {
  14.         return new BigInteger(arg);
  15.     }
  16. }

  17. class Memoizer3<A, V> implements Computable<A, V> {
  18.     private final ConcurrentHashMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
  19.     private final Computable<A, V> c;

  20.     public Memoizer3(Computable<A, V> c) {
  21.         this.c = c;
  22.     }

  23.     @Override
  24.     public V compute(final A arg) throws InterruptedException {
  25.         while (true) {
  26.             Future<V> f = cache.get(arg);
  27.             if (f == null) {
  28.                 Callable<V> eval = new Callable<V>() {
  29.                     public V call() throws InterruptedException {
  30.                         return c.compute(arg);
  31.                     }
  32.                 };

  33.                 FutureTask<V> ft = new FutureTask<V>(eval);
  34.                 f = cache.putIfAbsent(arg, ft);
  35.                 if (f == null) {
  36.                     f = ft;
  37.                     ft.run();
  38.                 }
  39.             }
  40.             try {
  41.                 return f.get();
  42.             } catch (ExecutionException e) {
  43.                 e.printStackTrace();
  44.             }
  45.         }
  46.     }
  47. }
这个版本还是有一些问题,比如没有缓存失效机制,并且异常的时候没有将Future从Map中移除.
不过已经说明了很多的用法.

这个while循环感觉很精妙
一旦 return f.get()发生ExecutionException异常,代码不会退出,而是会再次执行.

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29254281/viewspace-1782226/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2013-10-19

  • 博文量
    621
  • 访问量
    5957589