状态机模式中的Task与对象池

时间:2024-12-08 23:33:14

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的世界里面偷了点时间出来(这样想想还是有点刺激的)。