工作中经常会遇到这样的场景:一项复杂的工作耗费的时间比较长,为了避免重复这个过程,仅仅只需要一个线程去做这个工作就好了,其他的线程等待这个线程的工作结果,有几种方式可以实现这个模式,下面就总结了几种方式然后分析他们之间的不同。
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 的hashCode和equals方法
采取了同步方法来避免多线程同时请求,但是问题对于整个方法的同步这个粒度较大,是灵活性不够,其他的线程必须等待,下面有一种不用同步方法的实现方式。
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值,这样等待线程可以继续处理自己的其他逻辑,之后再回来查看是否复杂对象已经生产完毕。
对于缓存的结果数据如果较大,占据内存空间很多时,又不想太麻烦写一个缓存定时删除机制,可以使用weakReference和softReference来实现。WeakHashMap可以帮助实现这个功能。只需要将对象放入这个map,取出的时候首先取出Reference 实例,然后取出真实的值:(Reference) value).get()
在put的时候需要将值封装为WeakReference 的形式:
cache.put(key, new WeakReference(value));
分享到:
相关推荐
C#多线程编程实例 线程与窗体交互源码.rar
多线程处理是创建可以响应的智能客户端应用程序的重要部分,本文整理、概括与梳理了NET多线程处理的知识,主要包含:1)同步...7)使用任务处理 UI 线程和其他线程之间的交互;8)使用 Task 类,以及相关的示例代码等。
《C++面向对象多线程编程》共分13章,全面讲解构建多线程架构与增量多线程编程...第11章讨论C++对象在多线程环境中的行为和交互方式。第12章简单介绍多线程应用程序的测试技术。第13章对全书内容进行扼要地回顾与思考。
java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。
C#多线程编程实例 线程与窗体交互源码,该示例演示如何在线程安全的模式下调用Windows窗体上的控件。
分批取数 多线程数据处理
多线程实例化bean,创建多线程时可注入
C# winform 动态创建和 关闭多线程,基于VS2010的完整解决方案。 可运行
《MFC多线程的创建,包括工作线程和用户界面线程》全面讲解MFC多线程的创建,界面多线程与工作者多线程,多线程的起源、继承与派生,两多线程之间的区别与相同点,定时器与多线程的关系与异同(定时器是定时优先抢占...
多线程处理视频时,由于不同线程处理速度不同步,不同线程之间的数据传递需要进行缓冲处理。单块缓存的互斥操作或两块缓存的乒乓操作在大多数情况下不够用(数据流不平稳时丢数据);附件中利用C++实现了一个调用...
多线程 界面交互 backgroundWorker 用法 委托
基于vs2013平台,创建多线程的教程,基于vs2013平台,创建多线程的教程,基于vs2013平台,创建多线程的教程
Delphi多线程DB组件接口特性创建可自动回收的Query对象池,通过指定接口调用线程中的Query处理数据库操作,当请求结束后自动释放外部引用; 简要说明: TParamItem 管理存储过程的参数; ...
java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制
Revit二次开发 c# 本身revit无法进行多线程处理,本实例实现多线程处理
第11章讨论C++对象在多线程环境中的行为和交互方式。第12章简单介绍多线程应用程序的测试技术。第13章对全书内容进行扼要地回顾与思考。 本书适合用C++创建多线程组件和应用框架的程序员阅读。
Qt线程池,实现多个任务抢占多线程调度功能,用Qt事件循环解决假死