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

posted @ 2019-03-30 23:34  小小的平庸  阅读(294)  评论(0编辑  收藏  举报