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与对象池的更多相关文章

  1. 在C#中实现简单的对象池

    当我们频繁创建删除大量对象的时候,对象的创建删除所造成的开销就不容小觑了.为了提高性能,我们往往需要实现一个对象池作为Cache:使用对象时,它从池中提取.用完对象时,它放回池中.从而减少创建对象的开 ...

  2. GoLang设计模式06 - 对象池模式

    这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...

  3. 对象池模式(Object Pool Pattern)

    本文节选自<设计模式就该这样学> 1 对象池模式的定义 对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利 ...

  4. Java 中的对象池实现

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 最近在 ...

  5. 【JVM】Java 8 中的常量池、字符串池、包装类对象池

    1 - 引言 2 - 常量池 2.1 你真的懂 Java的“字面量”和“常量”吗? 2.2 常量和静态/运行时常量池有什么关系?什么是常量池? 2.3 字节码下的常量池以及常量池的加载机制 2.4 是 ...

  6. java对象池commons-pool-1.6详解(一)

    自己的项目中用到了 对象池 commons-pool: package com.sankuai.qcs.regulation.protocol.client; import com.dianping. ...

  7. 基于Apache组件,分析对象池原理

    池塘里养:Object: 一.设计与原理 1.基础案例 首先看一个基于common-pool2对象池组件的应用案例,主要有工厂类.对象池.对象三个核心角色,以及池化对象的使用流程: import or ...

  8. common-pool2对象池(连接池)的介绍及使用

    我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ...

  9. javascript设计模式学习之四——单例模式,缓存与对象池

    单例模式的定义:确保一个实例,并提供全局访问. 惰性单例的定义:只在需要的时候才创建对象. 在开发中,有些对象往往只需要一个,比如线程池.全局缓存.浏览器中的window对象等. java中的单例 关 ...

随机推荐

  1. http://www.rabbitmq.com/

    什么是RabbitMQ 官网http://www.rabbitmq.com/ 1.应用程序间健壮的消息发送 2.简单易用 3.可在所有主流操作系统运行 4.支持巨量的开发者平台 5.开源和商用双重支持 ...

  2. redis 设置认证

    server 使用帮助 [root@VM_7_88_centos src]# ./redis-server -h Usage: ./redis-server [/path/to/redis.conf] ...

  3. Swift:超炫的View Controller切换动画

    匿名社交应用Secret的开发者开发了一款叫做Ping的应用,用户可以他们感兴趣的话题的推送. Ping有一个很炫的东西,就是主界面和之间切换的动画做的非常的好.每次看到一个非常炫的动画,都不由得会想 ...

  4. 匹配yyyy-mm-dd日期格式的的正则表达式[转]

    转http://www.jb51.net/article/28034.htm 今天头让我修改个javascript方法,验证输入的日期是否符合要求.恩.我们的要求是yyyy-mm-dd这样的格式,其他 ...

  5. 阿里杨传辉的访问节选(oceanbase)

    皮皮(Q4): OceanBase第一个应用是收藏夹.最近,听说支付宝交易也用到了OceanBase.能否结合阿里的应用谈谈OceanBase的优势. 杨传辉(A4):相比传统的关系数据库,谈及Oce ...

  6. [label][WorldPress] 一个很方便查找定位WorldPress源代码位置的网址

    作为 WordPress 的新手,根本不熟悉那些函数究竟是什么作用的,所以就必须要去看源代码. 要去查看源代码,那么你就必须要熟悉 WordPress 下面文件的作用,以及那个文件中定义了有哪些函数? ...

  7. 6.nuget安装C#Driver驱动ZooKeeperNet

    一: C# 的Drivers   1. nuget上下载 zookeeper.Net   IWatcher是什么?: client 连接到 server 后,会在server上面注册一个watcher ...

  8. js练习计算器

    js练习计算器,支持鼠标点击.键盘操作 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &quo ...

  9. 执行计划--WHERE条件的先后顺序对执行计划的影响

    在编写SQL时,会建议将选择性高(过滤数据多)的条件放到WHERE条件的前面,这是为了让查询优化器优先考虑这些条件,减少生成最优(或相对最优)的执行计划的时间,但最终的执行计划生成过滤顺序还是决定这些 ...

  10. shell查找进程并终止

    创建kill.sh文件,内容如下: port= #一.根据端口号查询对应的pid,两种都行 pid=$(netstat -nlp | grep :$port | awk '{print $7}' | ...