`
real_junlin
  • 浏览: 19403 次
  • 性别: 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));

分享到:
评论

相关推荐

    C#分池检索 搜索引擎DEMO

    - **并行处理**:如果查询可以并行化,可以利用多线程或异步编程来加速处理。 通过学习这个DEMO,开发者可以了解到如何在C#中实现高效的对象池,并将其应用到实际的搜索引擎或查询系统中,以提高系统的整体性能和...

    单例模式

    这种模式在很多场景下都非常有用,比如控制资源的共享、管理配置对象或者创建昂贵的对象时,以减少系统开销。 单例模式的实现通常有几种方式: 1. 饿汉式(静态常量): 在类加载时就完成初始化,所以没有线程...

    高性能:池化技术的应用场景

    5. 对象池:对于一些昂贵的对象创建,如图形对象、网络套接字等,使用对象池可以显著减少创建和销毁对象的开销,提高系统性能。 三、常用热点面试题 1. 如何解释内存池的作用和优势? 2. 线程池如何提升系统的并发...

    《设计模式》各模式代码

    在多线程环境中,单例模式能有效管理资源,避免无谓的创建和销毁。 2. **工厂模式**:提供一个接口用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。简单工厂模式、工厂方法模式和抽象工厂模式各有侧...

    commons-pool2-2.6.2.jar和jedis-2.9.0.jar.zip

    3. 支持并发:设计考虑了多线程环境下的安全性,允许多个线程同时访问对象池。 4. 监控和统计:提供了详细的监控和统计信息,有助于调试和优化性能。 接下来是 `jedis-2.9.0.jar`,这是 Redis 客户端的一个 Java ...

    commons-pool-1.3和commons-dbcp-1.2.2.rar

    在三层架构中,DBCP作为数据访问层的一部分,负责在多线程环境下高效地管理和复用数据库连接,提升系统的并发处理能力。 在描述中提到的"SSH",通常指的是Struts、Spring、Hibernate这三个开源框架的缩写,它们是...

    23种设计模式C++实现

    5. **原型模式**:通过复制现有对象来创建新对象,避免了复杂且昂贵的构造过程。C++中可以利用`clone`函数实现浅复制或深复制。 6. **适配器模式**:使两个不兼容的接口能够协同工作。在C++中,可以通过继承或包含...

    JAVA设计模式之单例模式(完整版)[归类].pdf

    4. **双重检查锁定**(线程安全且效率较高):在多线程环境下安全创建单例,只有在instance为null时才进行同步,避免了不必要的同步开销。 上述四种方式分别体现了单例模式在不同需求下的权衡,开发者应根据具体...

    Design Pattern Quick Reference Card

    - **应用场景**: 当对象创建代价很大且对象会被重复使用时。 - **单线程执行(Single Threaded Execution)** - **定义**: 确保在单一的线程中执行特定的操作。 - **应用场景**: 当需要确保特定操作不会被并发执行...

    JAVA数据库工具类

    总的来说,这个数据库工具类利用DBCP连接池管理数据库连接,采用懒汉单例模式确保在需要时才创建连接,并通过线程绑定策略提高多线程环境下的安全性与效率。开发者可以通过配置`jdbc.properties`文件调整数据库连接...

    Design Patterns中英文版

    在多线程环境下,正确实现单例模式尤其重要,防止多个线程创建多个实例。 2. **工厂方法模式(Factory Method)**:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类...

    Hibernate 中文api文档下载

    11. **关联映射(Association Mapping)**:包括一对一、一对多、多对一和多对多四种类型,定义了对象之间的关系,如用户和角色、订单和商品等。 12. **继承映射(Inheritance Mapping)**:Hibernate支持单表继承...

    java性能优化.pdf

    异常处理在Java中是有成本的,它涉及到对象创建和堆栈回溯。异常应该仅用于错误处理,而不是作为程序流程的控制手段。 1.4 避免重复初始化变量 Java会在构造函数中默认初始化变量,如果不需要重新初始化,应避免在...

    java培训-Hibernate

    5. 关联映射:Hibernate支持一对一、一对多、多对一和多对多的关联映射,使得处理对象间的关联变得简单。 综上所述,Java培训-Hibernate课程旨在让开发者熟练掌握使用Hibernate进行数据库操作的技巧,通过理解并...

    Android代码-Android使用SurfaceView实现墨迹天气的风车效果.zip

    3. **启动绘制线程**:创建一个单独的线程来处理绘制,这样可以保证绘制过程不会阻塞UI线程。在这个线程中,你需要持续地调用`draw()`方法来更新画面,实现风车的旋转动画。 4. **实现风车动画**: - 使用`canvas....

    JDBC操作数据库辅助类

    - 提供线程安全的会话获取方式,比如单例模式或多线程环境下的线程局部变量。 - 配置和管理JdbcResourceManager,确保正确初始化连接池或其他资源。 在给定的文件中,我们有四个核心类:JdbcSession、...

    Java性能优化的45个细节

    2. **减少对象创建**:频繁的对象创建会增加垃圾收集压力,考虑使用对象池或者重用已有的对象。 3. **避免字符串连接操作**:使用StringBuilder或StringBuffer代替"+"连接大量字符串,特别是在循环中。 4. **使用...

    swift-史上最困难的游戏-ByObjective-C

    这对于处理网络请求、加载资源或者多线程交互等耗时操作至关重要。 综上所述,Swift以其强大的特性和语法,为游戏开发者提供了一个高效、安全的开发环境。通过掌握并巧妙运用这些知识点,即使是“史上最困难的游戏...

    Stackless Python 并发式编程介绍.doc

    2.1 微进程(tasklet):Stackless的核心是微进程,它们是轻量级的执行单元,可以快速地在彼此之间切换,而无需像线程那样进行昂贵的堆栈复制。 2.2 调度器(scheduler):调度器负责管理这些tasklet,决定何时以及...

    Hibernate面试题

    创建SessionFactory是一个昂贵的操作,因为它需要读取配置文件并初始化数据源,所以通常在应用启动时创建,并在整个应用运行期间保持不变。 - **Session**:Session是Hibernate与数据库交互的主要接口,它负责执行...

Global site tag (gtag.js) - Google Analytics