状态机模式中的Task与对象池
Task
抽象带来Task
首先,假设我们有这么一段逻辑:收到一个参数,先校验格式是否正确,再提取相关的参数出来,执行我们的事务,然后构建结果并返回。伪代码如下:
/**
* 一个engine类
**/
public class Engine {
public void init();
public void cancel();
public void restart();
// 处理逻辑
public void handleEvent(Param param) {
validateParam(param);
retrieveParam(param);
handleTransaction(param);
buildResult(param);
response(param);
}
}
在上面的handleEvent方法里面,处理逻辑与Engine并没有太多的联系,我们可以将这个逻辑抽离出来,或者说可以变得更抽象一点,如下:
/**
* 一个engine类
**/
public class Engine {
public void init();
public void cancel();
public void restart();
// 将处理逻辑抽象成Task类之后,Engine变得很简洁了
public void handleEvent(Param param) {
new Task().start(param);
}
}
class Task{
public void start(Param param){
validateParam(param);
retrieveParam(param);
handleTransaction(param);
buildResult(param);
response(param);
}
}
当然,如果engine被调用非常频繁,那么每次处理都新建一个Task类的话,会容易浪费内存,可以再优化一下性能,如下:
/**
* 一个engine类
**/
public class Engine {
public void init();
public void cancel();
public void restart();
// 减少新对象的创建可以将Task对象变成单例模式,或者将方法变成静态,这里取后者
public void handleEvent(Param param) {
Task.start(param);
}
}
class Task{
public static void start(Param param){
// ......
}
}
异步需要状态
如果上面的处理步骤有一个很耗时,例如handleTransaction方法需要10s才能返回,那么我们就需要将这个方法改造成异步的了,要不然就很影响engine的吞吐效率。这样,我们就需要一个状态标识符,用来标识handleTransaction方法是否执行完成,这个标识符可以放在engine里面,engine创建一个Map<Param,boolean>的成员变量,当handleTransaction方法执行完成产生回调,engine继续执行剩下的方法。
/**
* 一个engine类
**/
public class Engine {
private Map<Param, Boolean> statusMap = new HashMap<Param, Boolean>();
public void init();
public void cancel();
public void restart();
/**
* 处理逻辑
**/
public void handleEvent(Param param) {
boolean isInitHandle = statusMap.getOrDefault(param, Boolean.FALSE);
if (isInitHandle) {
Task.handleParamBeforeTransaction(param);
Task.handleTransaction(param);
}else{
Task.buildResultAndResponse(param);
}
}
}
class Task{
public static void handleParamBeforeTransaction(Param param){
validateParam(param);
retrieveParam(param);
}
public static void handleTransaction(Param param){
}
public static void buildResultAndResponse(Param param) {
buildResult(param);
response(param);
}
}
不过,这样就破坏了Engine类的抽象,Engine是管理者,但是却持有每次请求的状态,这样的管理类管的太细。比较好的抽象方式是将赋予Task状态,Engine直接管理Task。
/**
* 一个engine类
**/
public class Engine {
private Map<Param, Task> taskMap = new HashMap<Param, Task>();
public void init();
public void cancel();
public void restart();
/**
* 处理逻辑
**/
public void handleEvent(Param param) {
Task task = taskMap.computeIfAbsent(param, newParam ->new Task());
task.handle(param);
}
public void releaseTask(Param param){
taskMap.remove(param);
}
}
class Task{
private boolean isInit ;
private Engine engine;
public Task(Engine engine) {
this.engine = engine;
isInit=false;
}
public void handle(Param param) {
if (!isInit) {
handleParamBeforeTransaction(param);
handleTransaction(param);
}else{
buildResultAndResponse(param);
engine.releaseTask(param);
}
}
private void handleParamBeforeTransaction(Param param){
validateParam(param);
retrieveParam(param);
}
private void handleTransaction(Param param){
}
private void buildResultAndResponse(Param param) {
buildResult(param);
response(param);
}
}
Task含有一个isInit的成员变量,可以根据这个状态来判断应该执行什么操作,实际上这就是状态机模式了。我们进行这般改造之后,Engine的吞吐量会提高很多,而且抽象程度比较高。
不过,每次处理Task都需要新建一个对象出来,又回到了刚开始的时候。一旦Engine的tps上去,不断创建、释放的Task对GC肯定有不小的影响。
对象池
不断创建的Task实例,如果整个处理逻辑的时间都比较短,那么都可以在YGC时回收掉内存,如果时间比较长,那肯定有相当部分的Task实例被不断地移到老年代,这样很显然会导致更长时间的FGC。异步要求保留状态,而有状态就要求有新实例,Task的创建不可避免,可以考虑将Task实例放到对象池里面回收利用。
/**
* 一个engine类
**/
public class Engine {
private Map<Param, Task> taskMap = new HashMap<Param, Task>();
public void init();
public void cancel();
public void restart();
/**
* 处理逻辑
**/
public void handleEvent(Param param) {
Task task = taskMap.computeIfAbsent(param, newParam -> TaskPool.getTask());
task.init(this);
task.handle(param);
}
public void releaseTask(Param param) {
Task task = taskMap.remove(param);
TaskPool.releaseTask(task);
}
}
/**
Task实例的对象池
*/
class TaskPool {
private static Queue<Task> taskPool = new LinkedList<>();
public static Task getTask() {
Task task = taskPool.poll();
if (task == null) {
task = new Task();
}
return task;
}
public static void releaseTask(Task task) {
if(task!=null) {
task.reset();
taskPool.add(task);
}
}
}
class Task {
private boolean isInit;
private Engine engine;
// 由于Task放到对象池当中,需要提供一个init与reset方法,以回收利用
public void init(Engine engine) {
this.engine = engine;
isInit = false;
}
public void reset() {
this.engine = null;
isInit = false;
}
public void handle(Param param) {
if (!isInit) {
handleParamBeforeTransaction(param);
handleTransaction(param);
} else {
buildResultAndResponse(param);
engine.releaseTask(param);
}
}
private void handleParamBeforeTransaction(Param param) {
validateParam(param);
retrieveParam(param);
}
private void handleTransaction(Param param) {
}
private void buildResultAndResponse(Param param) {
buildResult(param);
response(param);
}
}
由于Task实例从对象池(会放置到老年代里面)当中获取,这样就可以减少从新生代创建的内存浪费。这样在engine高tps的情况下,可以减少些gc,相当于从JVM的世界里面偷了点时间出来(这样想想还是有点刺激的)。
状态机模式中的Task与对象池的更多相关文章
- 在C#中实现简单的对象池
当我们频繁创建删除大量对象的时候,对象的创建删除所造成的开销就不容小觑了.为了提高性能,我们往往需要实现一个对象池作为Cache:使用对象时,它从池中提取.用完对象时,它放回池中.从而减少创建对象的开 ...
- GoLang设计模式06 - 对象池模式
这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...
- 对象池模式(Object Pool Pattern)
本文节选自<设计模式就该这样学> 1 对象池模式的定义 对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利 ...
- Java 中的对象池实现
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 最近在 ...
- 【JVM】Java 8 中的常量池、字符串池、包装类对象池
1 - 引言 2 - 常量池 2.1 你真的懂 Java的“字面量”和“常量”吗? 2.2 常量和静态/运行时常量池有什么关系?什么是常量池? 2.3 字节码下的常量池以及常量池的加载机制 2.4 是 ...
- java对象池commons-pool-1.6详解(一)
自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...
- 基于Apache组件,分析对象池原理
池塘里养:Object: 一.设计与原理 1.基础案例 首先看一个基于common-pool2对象池组件的应用案例,主要有工厂类.对象池.对象三个核心角色,以及池化对象的使用流程: import or ...
- common-pool2对象池(连接池)的介绍及使用
我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ...
- javascript设计模式学习之四——单例模式,缓存与对象池
单例模式的定义:确保一个实例,并提供全局访问. 惰性单例的定义:只在需要的时候才创建对象. 在开发中,有些对象往往只需要一个,比如线程池.全局缓存.浏览器中的window对象等. java中的单例 关 ...
随机推荐
- JavaScript 分号使用总结
没有应该不应该,只有你自己喜欢不喜欢.JavaScript 语法长得 C-like 不代表它本质上和 C 是一类语言,所有直觉性的 "当然应该加分号" 都是保守的.未经深入思考的草 ...
- ie高版本浏览器不支持velocity的问题解决
<head><meta http-equiv="X-UA-Compatible" content="IE=5"></head> ...
- Java线程死锁查看分析方法
如何查看是否有Java线程死锁?下面介绍两种方法. 一.Jconsole Jconsole是JDK自带的图形化界面工具,使用JDK给我们的的工具JConsole,可以通过打开cmd然后输 ...
- 441. Arranging Coins
static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...
- 课堂限时训练-简易计算器·mini dc
课堂限时训练-简易计算器·mini dc 实验题目 采用后缀表达式法,设计一个建议计算器,实现+.-.*./四种运算. 代码实现 码云链接 关键代码部分及结果如下: 实验分析 首先,分析一下后缀表达式 ...
- POJ2031 Building a Space Station 2017-04-13 11:38 48人阅读 评论(0) 收藏
Building a Space Station Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8572 Accepte ...
- poj2115 Looooops 扩展欧几里德的应用
好开心又做出一道,看样子做数论一定要先看书,认认真真仔仔细细的看一下各种重要的性质 及其用途,然后第一次接触的题目 边想边看别人的怎么做的,这样做出第一道题目后,后面的题目就完全可以自己思考啦 设要+ ...
- Tomcat 系统架构与设计模式1
从 Tomcat 如何分发请求.如何处理多用户同时请求,还有它的多级容器是如何协调工作的角度来分析 Tomcat 的工作原理,这也是一个 Web 服务器首要解决的关键问题 Tomcat 总体结构 To ...
- java提示找不到或无法加载主类
背景 默许jdk的配置大家都没有问题,执行java,javac无报错,但今天在尝试在本地起来kafka的时候,提示java 找不到或无法加载主类,然后日志中提示 Files 找不到或无法加载主类:C: ...
- apache ap 并发测试工具
可以使用 apache httpd 软件包里的 ab.exe 简单的做些网站的性能测试, ab.exe 是一个命令工具,所以不能双击运行, 在 cmd 下运行: ab.exe -n 1000 -c 5 ...