`
real_junlin
  • 浏览: 18812 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

多线程处理之间昂贵对象创建交互

阅读更多

 

工作中经常会遇到这样的场景:一项复杂的工作耗费的时间比较长,为了避免重复这个过程,仅仅只需要一个线程去做这个工作就好了,其他的线程等待这个线程的工作结果,有几种方式可以实现这个模式,下面就总结了几种方式然后分析他们之间的不同。

1通过future实现

基本代码如下:

 

public class CacheTest<T> {
 
    Map<Request, T> cache = new HashMap<Request, T>();
   
    synchronized T requestInvoke(Request r){
       T result = cache.get(r);
       if( result != null){
           return result;
       }
       result = doSomeThing();
       cache.put(r, result);
       return result;
    }
   
    T doSomeThing(){
       。。。
    }
   
}
 

需要注意的是需要重写Request hashCodeequals方法

采取了同步方法来避免多线程同时请求,但是问题对于整个方法的同步这个粒度较大,是灵活性不够,其他的线程必须等待,下面有一种不用同步方法的实现方式。

 

 

class CacheTest2<T> {
    Map<Request, Future<T>> cache = new ConcurrentHashMap<Request, Future<T>>();
   
    T requestInvoke(final Request r){
       Future<T> f = cache.get(r);
       if(f == null){
           Callable<T> v = new Callable<T>(){
              @Override
              public T call() throws Exception {
                  return doSomeThing(r);
              }
           };
           FutureTask<T> ft = new FutureTask<T>(v);
           f = cache.putIfAbsent(r, ft);
           if(f == null) {
              f = ft;
              ft.run();
           }
       }
       return f.get();
    }
   
    T doSomeThing(Request r){
       return null;
    }
   
}
 

第一条线程首先在缓存中查找是否有相应的对象(futuredata),如果不存在的话那这个线程就是第一条线程,是要去生产realdata的,他会继续往下执行生产过程,

 

2对象锁

基本原理是每条线程来的时候检测缓存汇总有没有处理好的结果,或者是处理中的结果(使用临时对象,如果是临时对象,则线程会等待处理结果),第一条线程来将临时对象放进缓存,然后自己去生成真实的返回对象,整个工作完成后把真实对象放进缓存,然后唤醒其他的线程。

 

class CacheTest2{
    //
    Private static Object pendingGenerationMarker = new Object();
Map cache = new HashMap(); 
Object requestInvoke(Request r){
       Object value;
      
       synchronized (cache) {
           do {
           value = cache.get(r);
           if (value == null) {
              cache.put(r, pendingGenerationMarker);
               break;
           } else if (value == pendingGenerationMarker) {
               try {
              cache.wait();//此处的变种是可以设置等待时间
               } catch (InterruptedException e) {
               }
               continue;
           } else {
              return value;
           }
           } while (true);
       }
      
       try {
           value = doSomeThing(r);
       } catch (Exception e) {
       }finally{
           synchronized (cache) {
               cache.put(r, value);
               cache.notifyAll();
           }
       }
       return value;
    }
    Object doSomeThing(Request r){
       …
    }
}
 

这种方式比较灵活之处是在等待时间的处理方面,当等待时间超时后,逻辑处理可以返回null值,这样等待线程可以继续处理自己的其他逻辑,之后再回来查看是否复杂对象已经生产完毕。

 

对于缓存的结果数据如果较大,占据内存空间很多时,又不想太麻烦写一个缓存定时删除机制,可以使用weakReferencesoftReference来实现。WeakHashMap可以帮助实现这个功能。只需要将对象放入这个map,取出的时候首先取出Reference 实例,然后取出真实的值:(Reference) value).get()

put的时候需要将值封装为WeakReference 的形式:

cache.put(key, new WeakReference(value));

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics