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. RocketMQ:Cannot allocate memory

    使用Storm本地模式消费RocketMQ数据的时候, 消费一点数据之后,就会出现如下错误: Java HotSpot(TM) 64-Bit Server VM warning: INFO: os:: ...

  2. HDU 2036 改革春风吹满地 (计算几何)

    题意:你懂得. 析:没什么可说的,求面积用叉乘,尽量不要用海伦公式,因为计算量大,而且精度损失. 代码如下: #include <iostream> #include <cstdio ...

  3. C++中const在函数中的用法

    1.const放在函数前面 如果我们的函数的返回值是以 指针形式 返回的,如果在函数前面加上const修饰,则表示指针指向的内容是不能被改变的,并且接收返回值的 指针变量必须是const修饰的,例如: ...

  4. java并发编程实战:第十六章----Java内存模型

    一.什么是内存模型,为什么要使用它 如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果 编译器把变量保存在本地寄存器而不是内存中 编译器中生成的指令顺序,可以与源代码中的顺 ...

  5. handsontable-developer guide-cell editor

    单元格编辑 cell editor renderer:展示数据:editor:改变数据:renderer用一个函数表示:后者有一系列的操作,需要用class来表示: EditorManager han ...

  6. cenos 安装nginx并添加到service

    系统平台:CentOS release 6.6 (Final) 64位. 一.安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c++ libtoo ...

  7. Hadoop 集群配置记录小结

    Hadoop集群配置往往按照网上教程就可以"配置成功",但是你自己在操作的时候会有很多奇奇怪怪的问题出现, 我在这里整理了一下常见的问题与处理方法: 1.配置/etc/hosts ...

  8. Jenkins的多个任务并串联参数传递

    Jenkins的多个任务并串联参数传递 Parameterized Trigger Plugin插件可以使多个job连接的时候可以传递一些job相关的参数信息. 1.Parameterized Tri ...

  9. ASP.NET IIS 支持PUT、DELETE请求

    IIS 本身不支持PUT.DELETE请求,但可以通过一下方法修改进而达到目的 删除IIS安装的WebDav模块,选择你的项目,右边有个“模块”,双击它:找到WebDavModule,删除它(不推荐, ...

  10. (C#版本)提升SQlite数据库效率——开启事务,极速插入数据,3秒100万,32秒1000万条数据

    SQLite插入数据效率最快的方式就是:开启事务  +   insert语句  +  关闭事务(提交) 利用事务的互斥性,如果在批量的插入操作前显式地开启一次事务,在插入操作结束后,提交事务,那么所有 ...