spring线程池的使用
为了尽量减少耗时操作对Action执行的影响,使用TaskExecutor线程池来管理耗时任务,作为后台进程执行,从而解决了问题。
场景:
使用了Struts和Spring,但Struts的Action并未交给Spring容器管理,Spring容器仅仅用来管理Dao。
要求:
对每个Action,实现向数据库写入Log功能,最好做到不要影响正常的操作流程 。Log的内容是此Action的请求参数,由客户端决定。
分析:
如何截获或者使得Action执行前后做写入Log动作并非关键,难点在于“不要影响正常的操作流程”。可以将Action交给Spring容器管理,利用AOP功能将向要执行的动作添加在Action执行前后;或者为所有Action添加一含有写入Log动作的父类。
不影响正常的操作流程,最理想的状态就是实现导弹发射出去后不管的功能。即在Action执行的某个步骤中,执行一下写入Log的命令,然后继续执行后继任务,写入Log任务让其自己去执行,无需等待返回信息。所以写入Log功能就不能与Action处于同一进程,否则Log任务会抢占资源而让Action的后续任务处于等待状态。
势必需要启动新的进程来执行Log任务。对于一个Action还不算复杂,多个呢?启动的新进程如何管理就成了新的问题。如果解决了这个问题,原来的问题就显得很容易了。
实现:
根据以上分析,希望有这么一个进程提供者,有这样的功能:能接受任务;当有可用线程时,任务获得调度执行;无可用资源时,任务处于等待状态;任务执行完成之后,释放进程资源给进程管理者。这就是一个进程池的基本功能。正好Spring定义了--TaskExecutor线程池(想想Spring还真是无所不有啊!),为各种进程池提供了统一的用户接口。
这里就不去分析各种不同线程池的功能了。简单地讲一下对上面提出要求的解决步骤:
1 定义写入Log任务;
线程池可接受的任务都必须是实现了Runable接口的类,因此定义一个写入Log的任务如下:
class LogTask implements Runnable ...{
private AccessLog accessLog = null;
private AccessLogDao accessLogDao = null;
public LogTask(AccessLog accessLog, AccessLogDao accessLogDao) ...{
this.accessLog = accessLog;
this.accessLogDao = accessLogDao;
}
public void run() ...{
accessLogDao.addLog(accessLog);
}
}其中的AccessLog是一个简单Bean,定义了需要写入的各种信息;AccessDao专门向数据库中写入AccessLog中的内容提供服务。在new一个LogTask的时候,必须提供这两个参数。
2 实现用TaskExcutor执行写入Log任务;
首先指定TaskExcutor接口的实现者。将具体的TaskExcutor用定义的方式配置并由Spring容器管理:
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="2" />
<property name="keepAliveSeconds" value="200" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="60" />
</bean>可以看到,具体使用的是ThreadPoolTaskExecutor。这样更换别的线程池也比较方便。
将任务加入到进程池并执行则比较简单:
LogTask logTask = new LogTask(accessLog, accessLogDao);
taskExecutor.execute(logTask);加入任务后,进程池会根据调度算法来执行任务(调度算法也可以自定义)。
3 定义服务接口,并实现;
定义一个Action可以方便使用的接口:
public interface AccessLogService ...{
public void writeLog(HttpServletRequest request);
}它接受的参数就是包含了客户端请求参数的HttpServletRequest。这样就不需要Action去作参数分析了,具体的工作留给AccessLogService完成。
实现此接口: public class AccessLogServiceImpl implements AccessLogService ...{
/**//*
* (non-Javadoc)
* @see my.test.service.AccessLogService#writeLog(javax.servlet.http.HttpServletRequest)
*/
public void writeLog(HttpServletRequest request) ...{
//分析request,创建AccessLog对象
AccessLog accessLog = new AccessLog();
//以下略
writeLog(accessLog);
}
//-------------------------------------------------------------------------
/**//**
* 写入Log信息<br>
*
* @param accessLog
*/
private void writeLog(AccessLog accessLog) ...{
LogTask logTask = new LogTask(accessLog, accessLogDao);
taskExecutor.execute(logTask);
}
/**//** DAO */
private AccessLogDao accessLogDao = (AccessLogDao) getDaoByName("accessLogDao");
/**//** 线程池 */
private TaskExecutor taskExecutor = (TaskExecutor) getDaoByName("taskExecutor");
//-------------------------------------------------------------------------
/**//**
* 任务定义
*
*/
class LogTask implements Runnable ...{
private AccessLog accessLog = null;
private AccessLogDao accessLogDao = null;
public LogTask(AccessLog accessLog, AccessLogDao accessLogDao) ...{
this.accessLog = accessLog;
this.accessLogDao = accessLogDao;
}
public void run() ...{
accessLogDao.addLog(accessLog);
}
}
}这里的任务被作为内部类定义的。
4 为Action加入写入Log功能。
直接使用策略模式,为所有Action添加一父类,在父类中调用写入Log功能,而具体的Action执行流程由子类决定。
public abstract class BaseAction extends Action ...{
public ActionForward execute(
ActionMapping mapping,
ActionForm actionForm,
HttpServletRequest request,
HttpServletResponse response)
throws Exception ...{
try ...{
ActionForward forward = _execute(mapping, actionForm, request, response);
} catch (Exception e) ...{
throw e;
} finally ...{
AccessLogService als = new AccessLogServiceImpl();
als.writeLog(request);
}
}
//子类必须实现此方法
public abstract ActionForward _execute( ActionMapping mapping,
ActionForm actionForm,
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}子类中只需实现抽象方法_execute即可。在执行完子类的_execute之后,才会将写入Log的任务放入进程池,然后返回。具体任务何时被执行,则由进程池管理。
通过分析执行时log可知,Action返回之后,写入Log任务才被执行。也就是说,这样做了之后,很大程度上减少了对Action执行流程的干扰,同时还完成了比较耗时的写数据库的工作。
总结:这只是为了解决项目中的某个特殊的需求而做的,可能也不是什么好的通用的办法。但它也给我们以后解决比如需要后台执行之类的要求提供了一种可行的方案。
- 实用小工具,教你轻松转化Python通用数据格式
- 数据工程师常用的几个小工具(附python源代码)
- R语言的三种聚类方法
- 技能 | R语言的igraph画社交关系图示例
- 魔兽世界中招:一条命令行就能劫持你的游戏!
- R语言 apply函数家族详解
- 基于R语言的梯度推进算法介绍
- R语言数据可视化综合指南
- 关于CLR内存管理一些深层次的讨论[上篇]
- 关于CLR内存管理一些深层次的讨论[下篇]
- Python渗透工具的架构探讨
- 提供第三种代码生成方式——通过自定义BuildProvider为ASP.NET提供代码生成
- 小心,Android木马工具SpyNote免费啦!远程监听就是这么简单
- R语言的kmeans客户细分模型聚类
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 【动手学深度学习笔记】之图像分类数据集(Fashion-MNIST)
- 探讨缓存行与伪共享
- Stream 流解读
- 3 分钟生成一个单元测试报告,这个样式爱了
- 使用pymouse模块时候报错No module named 'windows'
- GO用内置包写爬虫
- rsyslog详解实战和避坑
- 删除行对MySQL序列有这么多影响?
- 论C++如何优雅的使用数组
- Dubbo如何通过SPI提高框架的可扩展性?
- 你必须熟练使用的30个有用Python代码片段
- git上过滤一些编辑器生成的文件
- Mongoose 实现关联查询和踩坑记录
- UWP开发01之Windows UI2.x
- 前端模块化:CommonJS,AMD,CMD,ES6