zoukankan      html  css  js  c++  java
  • 多线程缓存事例

    注:文章示例由上而下,安全级别越高。

    示例1.

             public interface Computable<A,V>{
    		 V compute(A arg) throws InterruptedException;
    	 }
    	 public class ExpensiveFunction implements Computable<String,BigInteger>{
    		 public BigInteger compute(String arg){
    			 //在经过长时间的计算后
    			 return new BigInteger(arg);
    		 }
    	 }
    	 public class Memoizer1<A,V> implements Computable<A, V>{
    		 @GuardedBy("this")
    		 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;
    		 }
    	 }        
    

     问题是:HashMap 不是线程安全的,因此采用的是将compute方法进行同步。但是这样只能保证每次只有一个线程执行compute方法,有明显的可伸缩性问题。

    示例2.

            public class Memoizer2<A,V> implements Computable<A, V>{
    		 private final Map<A,V> cache = new ConcurrentHashMap<A, V>();//线程安全,高效
    		 private final Computable<A,V> c;
    		 private Memoizer2(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;
    		 }
    	 }
    

     示例2问题在于:如果某个线程启动了一个开销很大的计算,而其他线程并不知道这个计算正在进行,那么很可能会重复这个计算。

    示例3.

            public class Memoizer3<A,V> implements Computable<A, V>{
    		 private final Map<A,Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
    		 private final Computable<A,V> c;
    		 private Memoizer3(Computable<A,V> c){
    			 this.c = c;
    		 }
    		 
    		 public V compute(final A arg) throws InterruptedException{
    			 Future<V> f = cache.get(arg);//检查计算是否存在
    			 if(f == null){
    				 Callable<V> eval = new Callable<V>(){
    					 public V call() throws InterruptedException{
    						 return c.compute(arg);
    					 }
    				 };
    				 FutureTask<V> ft = new FutureTask<V>(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<A,V> implements Computable<A, V>{
    		 private final Map<A,Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
    		 private final Computable<A,V> c;
    		 private Memoizer4(Computable<A,V> c){
    			 this.c = c;
    		 }
    		 
    		 public V compute(final A arg) throws InterruptedException{
    			 while(true){
    				 Future<V> f = cache.get(arg);//检查计算是否存在
    				 if(f == null){
    					 Callable<V> eval = new Callable<V>(){
    						 public V call() throws InterruptedException{
    							 return c.compute(arg);
    						 }
    					 };
    					 FutureTask<V> ft = new FutureTask<V>(eval);//不存在,创建FutureTask
    					 f = ft;
    					 cache.putIfAbsent(arg, ft);//注册到map中, putIfAbsent原子方法
    					 ft.run();//开始执行计算
    				 }
    				 try {
    					 return f.get(); //获得最后计算结果
    				 } catch (ExecutionException e) {
    					 
    				 }
    			 }
    		 }
    	 }
    
  • 相关阅读:
    vb中5种打开文件夹浏览框的方法总结(转)
    js 日期
    正则表达式
    HTML中meta的作用
    Java操作ElasticSearch
    Elastic安装
    Elasticsearch基本操作
    ubootfs2410移植(转)
    内核驱动系列字符设备驱动流程
    linux 内核移植
  • 原文地址:https://www.cnblogs.com/cnmenglang/p/6266289.html
Copyright © 2011-2022 走看看