注:文章示例由上而下,安全级别越高。
示例1.
public interface Computable { V compute(A arg) throws InterruptedException; } public class ExpensiveFunction implements Computable{ public BigInteger compute(String arg){ //在经过长时间的计算后 return new BigInteger(arg); } } public class Memoizer1 implements Computable { @GuardedBy("this") private final Map cache = new HashMap (); private final Computable c; public Memoizer1(Computable 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; } }
问题是:HashMap 不是线程安全的,因此采用的是将compute方法进行同步。但是这样只能保证每次只有一个线程执行compute方法,有明显的可伸缩性问题。
示例2.
public class Memoizer2 implements Computable { private final Map cache = new ConcurrentHashMap ();//线程安全,高效 private final Computable c; private Memoizer2(Computable 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; } }
示例2问题在于:如果某个线程启动了一个开销很大的计算,而其他线程并不知道这个计算正在进行,那么很可能会重复这个计算。
示例3.
public class Memoizer3 implements Computable { private final Map> cache = new ConcurrentHashMap >(); private final Computable c; private Memoizer3(Computable c){ this.c = c; } public V compute(final A arg) throws InterruptedException{ Future f = cache.get(arg);//检查计算是否存在 if(f == null){ Callable eval = new Callable (){ public V call() throws InterruptedException{ return c.compute(arg); } }; FutureTask ft = new FutureTask (eval);//不存在,创建FutureTask f = ft; cache.put(arg, ft);//注册到map中 ft.run();//开始执行计算 } try { return f.get(); //获得最后计算结果 } catch (ExecutionException e) { } } }
FutureTask :表示一个计算的过程,这个过程可能已经计算完成,也可能正在进行。如果有结果可用,那么FutureTask.get将立即返回结果,否则会一直阻塞,直到结果计算出来再将其返回。
示例3问题在于:仍然存在两个线程重复计算的问题。因为if语句块是复合操作(“若没有则添加”),无法保证原子性。解决这个问题也很简单,只要使用ConcurrentMap 中的原子方法 putIfAbsent就可以啦。
请看示例4
public class Memoizer4 implements Computable { private final Map> cache = new ConcurrentHashMap >(); private final Computable c; private Memoizer4(Computable c){ this.c = c; } public V compute(final A arg) throws InterruptedException{ while(true){ Future f = cache.get(arg);//检查计算是否存在 if(f == null){ Callable eval = new Callable (){ public V call() throws InterruptedException{ return c.compute(arg); } }; FutureTask ft = new FutureTask (eval);//不存在,创建FutureTask f = ft; cache.putIfAbsent(arg, ft);//注册到map中, putIfAbsent原子方法 ft.run();//开始执行计算 } try { return f.get(); //获得最后计算结果 } catch (ExecutionException e) { } } } }