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执行流程的干扰,同时还完成了比较耗时的写数据库的工作。
总结:这只是为了解决项目中的某个特殊的需求而做的,可能也不是什么好的通用的办法。但它也给我们以后解决比如需要后台执行之类的要求提供了一种可 行的方案。
spring线程池的使用相关推荐
- spring 线程池_Spring线程池服务
spring 线程池 线程池对于执行同步和异步过程非常重要. 本文介绍如何使用Spring开发和监视线程池服务. 创建线程池已通过两种替代方法进行了说明. 二手技术 : JDK 1.6.0_21 春天 ...
- Spring线程池开发实战
Spring线程池开发实战 作者:chszs,转载需注明. 作者博客主页:http://blog.csdn.net/chszs 本文提供了三个Spring多线程开发的例子,由浅入深,由于例子一目了然, ...
- spring线程池的理解和使用
1.spring线程池 <bean id="taskExecutor" class="org.springframework.scheduling.concurre ...
- Spring线程池服务
线程池对于执行同步和异步过程非常重要. 本文介绍如何使用Spring开发和监视线程池服务. 创建线程池已通过两种替代方法进行了说明. 二手技术 : JDK 1.6.0_21 Spring3.0.5 M ...
- spring线程池使用
为何使用多线程 对于复杂的业务逻辑下,有时候需要使用多线程执行,以优化项目的执行速度 单线程同步执行的场景中,如果前边出现异常,会导致所有任务中断,异步执行没有这样的问题 多线程实现方式:线程池 减少 ...
- Spring 线程池使用
Spring 中默认自带线程池_org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor,一般有可以直接使用,这是时候使用的是默 ...
- Spring线程池异步传递MDC信息
目录 1. 什么是MDC 2. 引入MDC打印步骤 2.1 pom依赖 2.2 log4j2打印日志配置文件 3 步骤演示 3.1 单线程业务使用示例 postman查询示例 查询代码 查询日志 3. ...
- spring线程池 java_Java 中几种常用的线程池
概述: 在java内置API中操作线程所用到的类为Thread.创建线程一般有两种方式, 继承Thread方式 实现Runnable方式,并以runnable作为target创建Thread 在And ...
- Spring 线程池的使用和配置
@Configuration @EnableAsync //开启异步调用 public class AsyncExecutorConfig {@Value("${thread.number} ...
最新文章
- ARM体系结构及内核回顾总结(一)
- php 二叉树判断节点的位置,PHP实现判断二叉树是否对称的方法
- Head First JSP---随笔五
- intro to cs with python_CS 105 – Intro to Computing Non-Tech Spring 2019
- Xcode 升级后,常常遇到的遇到的警告、错误,解决方法(转)
- Flutter AnimatedBuilder 的基本使用
- 数据仓库流程和代码说明
- linux中指令简约版
- 宏基笔记本升级bios(2012-12-28-bd 写的日志迁移
- plist 文件详解
- 程序员慵懒的周末:不想出门还想吃肯德基香辣鸡翅?
- 一个大学老师的2020
- 最新前端开发面试题集合(非常全面)
- 写一个python函数 用cuda加速
- Allegro阻抗分析指导书
- 办公软件不能打印能打印测试页,excel2010无法打印的解决方法
- 基于快速傅里叶变换实现的狗声识别器(人工智能)
- java打怪升级地图
- 读懂python中的self
- 如何科学应对宝宝秋季腹泻?