有依赖的并发任务执行-代码实践(工厂、模板模式、线程池的使用)-优化篇

1、背景

一组任务并发执行,如任务A、B、C、D,其中有状态依赖关系,如D依赖C,即C任务执行完毕后,D任务才能开始执行。

 

2、实现思路

2.1、接口定义

2.1.1、业务执行接口定义

(1) 业务执行(业务处理相关接口)( IDataHolder)
(2) 缓存清理接口(IReleaseCache)

2.1.2、任务管理接口定义

(1) 任务执行管理接口(IExecute)
(2) 锁管理接口(任务依赖管理)(ILockMgr)

以上接口设计,接口各司其能,耦合性降低,简单明确,易于扩展。其中IDataHolder提供AbstractDataHolder抽象类实现,其中定义模板方法(定义为final),固定业务处理行为。

2.2、工厂生成实例

定义DataHolderFactory生成不同类型的IDataHolder实例

2.3、任务有向图设计

(1) 定义任务节点(Task),包含具体要执行的任务,父亲节点(标识依赖的任务),孩子节点(构建任务树),及锁成员(标识本任务是否执行完毕)
(2) 定义有向图(TaskGraph),包含头结点(Task,仅标识任务开始,无真正要执行的业务),“最宽”层的节点个数(作为线程池执行任务时的最大可并发数,可节省线程资源,减少依赖任务执行时的无效等待)。提供对外接口,返回要执行的任务列表(层次/广度遍历图),这样线程池在执行时会优先提交父任务,尽量减少子任务等待父任务的情况,提升效率

2.4、任务执行管理类

TaskMgr负责整体调度所有的任务,接收输入为需要处理的任务类型Map

2.5、对外服务类

DiffService负责对外提供数据处理服务,接收参数场景上下文,初始化任务类型依赖关系,调起TaskMgr中调度任务接口

2.6、设计特点

1、简洁、分工明确、结构清晰
2、耦合性低,相比于之前版本,任务之间的依赖关系上移,更加清晰明确
3、资源占用较少,线程池有序处理任务,限制最大线程数量,分批执行任务,同时通过合理设置任务提交顺序及线程池最大线程数量,提交吞吐量
4、扩展性强,每次扩展只需要修改DiffService中构建任务依赖部分,及扩展具体业务执行类即可,满足开闭原则。

2.7、缺陷

1、任务调度还是被Task管理,而不是由上层调度框架控制,这样吞吐量及性能无法达到最优
2、线程池执行任务时,某些情况依然可能出现大量的等待,造成资源浪费
最终演进,需参考Spark的任务调度框架,Stage切分,状态管理,任务调度等实现最优的资源分配。

 

3、代码实现

3.1、业务执行接口定义

public interface IDataHolder extends IReleaseCache, ITimeMgr
{

    /**
     * getOriginalDatas 获取WhatIf调整前后的源数据<br>
     * 
     */
    public Map<EnumWhatIfStep, Object> getOriginalDatas();
    
    /**
     * 设置原始数据<br>
     * 
     */
    public void setOriginalData(EnumWhatIfStep whatIfStep, Object object);

    /**
     * 生成差异数据及结果统计信息<br>
     */
    public void diffAndStats();

    /**
     * DIFF数据、Summary信息持久化 persist<br>
     */
    public void persist();
    
    /**
     * processDataDiff<br>
     * 进行数据差异对比,并把结果入库<br>
     */
    public void processDataDiff();
    
}

public interface IReleaseCache
{
    /**
     * releaseCacheInMemory<br>
     * 释放内存中的缓存<br>
    void releaseCacheInMemory();
    
    /**
     * clearDataInDb<br>
     * 清除数据库中的数据<br>
     */
    void clearDataInDb();
    
}

3.2、工厂构建业务任务实例及抽象类

public class DataHolderFactory
{
    /**
     * createDataHolder <br>
     * 
     * @param type 类型
     * @param dataHolders 依赖的dataHolder
     * @return IDataHolder
     */
    public static IDataHolder createDataHolder(EnumBusinessType type)
    {
        if (type == EnumBusinessType.xxx)
        {
            return new XXXDataHolder();
        }
        else if (type == EnumBusinessType.xxx)
        {
            return new XXXDataHolder();
        }
        else if (type == EnumBusinessType.xxx)
        {
            return new XXXDataHolder();
        }
        else if (type == EnumBusinessType. xxx)
        {
            return new XXXDataHolder ();
        }
        return null;
}
public abstract class AbstractDiffDataHolder<T> implements IDataHolder
{
    private static final Logger LOGGER = LoggerFactory.getLog(AbstractDiffDataHolder.class);

    /**
     * 任务完成需要的最大毫秒数
     */
    private static final long TASK_COMPLETE_MAX_MILLISECONDS = 300000L;

    protected Object originalDataBeforeIf;

    protected Object originalDataAfterIf;

    /**
     * Diff数据缓存
     */
    @Getter
    @Setter
    private List<T> diffCache = new ArrayList<T>();

    /*
     * 统计信息缓存
     */
    @Getter
    @Setter
    private BaseSummary summary;

    /**
     * startDataDiff<br>
     * 开始数据对比,并结果入库<br>
     */
    @Override
    public final void processDataDiff()
    {
        // 1、清除上次生成的diff数据
        this.clearDataInDb();

        // 2、获取调整前和调整后的源数据
        Map<EnumWhatIfStep, Object> originalDatas = this.getOriginalDatas();

        // 3、设置源数据到originalDataBeforeIf和originalDataAfterIf中
        Object originalDataBefore = originalDatas.get(EnumWhatIfStep.BEFORE);
        Object originalDataAfter = originalDatas.get(EnumWhatIfStep.AFTER);
        if (null == originalDataBefore || null == originalDataAfter)
        {
            LOGGER.error("Invalid data, original data(before or after) is empty, task finish.");
            return;
        }
        this.setOriginalData(EnumWhatIfStep.BEFORE, originalDataBefore);
        this.setOriginalData(EnumWhatIfStep.AFTER, originalDataAfter);

        // 4、数据对比,产生对比差异数据diff data和统计数据Summary data
        this.diffAndStats();

        // 5、结果入库,差异数据、统计数据入库
        this.persist();

        // 6、释放缓存加快GC
        releaseCacheInMemory();

    }

    @Override
    public final void releaseCacheInMemory()
    {
        originalDataBeforeIf = null;
        originalDataAfterIf = null;
        diffCache = null;
        summary = null;
    }
    
    @Override
    public long requiredMilliSeconds()
    {
        return TASK_COMPLETE_MAX_MILLISECONDS;
    }
    
    @Override
    public abstract void setOriginalData(EnumWhatIfStep whatIfStep, Object object);

    @Override
    public abstract Map<EnumWhatIfStep, Object> getOriginalDatas();

    @Override
    public abstract void diffAndStats();

    @Override
    public abstract void persist();

    @Override
    public abstract void clearDataInDb();

}

3.3、任务接口定义

/**
 * ITimeMgr<br>
 */
public interface ITimeMgr
{
    /**
     * requiredMilliSeconds<br>
     * 
     * @return 需要的毫秒数
     */
    long requiredMilliSeconds();
}

public interface IExecute
{
    /**
     * preExecute<br>
     * 执行前准备<br>
     */
    void preExecute();
    
    /**
     * postExecute<br>
     * 执行后处理<br>
     */
    void postExecute();
}

public interface ILockMgr
{
    /**
     * initLock<br>
     */
    void initLock();
    
    /**
     * getLock<br>
     */
    CountDownLatch getLock();
    
    /**
     * releaseLock<br>
    void releaseLock();
}

/**
 * ICreateTask<br>
 * 
 */
public interface ICreateTask<T, R>
{
    /**
     * createTaskInstance<br>
     * 根据任务类型创建任务实例<br>
     * 
     * @param type 创建任务实例
     * @return 任务实例
     */
    public abstract T createTaskInstance(R type);
}

3.4、任务有向图构建

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import lombok.Getter;
import lombok.Setter;

public class Task<T extends ITimeMgr> implements IExecute, ILockMgr
{
    private static final Logger LOGGER = LoggerFactory.getLog(Task.class);
    
    private static final TimeUnit TIME_UNIT_MILLISECONDS = TimeUnit.MILLISECONDS;
    
    /**
     * 当前任务
     */
    @Getter
    @Setter
    private T task;
    
    /**
     * 依赖的父任务列表
     */
    @Getter
    private List<Task<T>> parents = new LinkedList<>();
    
    /**
     * 依赖此任务的任务列表
     */
    @Getter
    private List<Task<T>> children = new LinkedList<>();
    
    /**
     * 当前任务的锁
     */
    private CountDownLatch countDownLatch;
    
    /**
     * 构造函数<br>
     * 
     * @param task 具体任务
     */
    public Task(T task) 
    {
        // 初始化任务
        this.task = task;
    }

    @Override
    public void initLock()
    {
        this.countDownLatch = new CountDownLatch(1);
    }

    @Override
    public CountDownLatch getLock()
    {
        return this.countDownLatch;
    }
    
    @Override
    public void releaseLock()
    {
        if (this.countDownLatch != null)
        {
            this.countDownLatch.countDown();
        }
    }

    @Override
    public void preExecute()
    {
        // 执行任务前需要等待依赖的父任务全部执行完毕
        for (Task<T> parent : parents) 
        {
            // 获取父任务的锁
            CountDownLatch latch = parent.getLock();
            if (latch == null)
            {
                continue;
            }
            // 存在锁,需要等待锁释放
            try
            {
                // 等待父任务执行完毕, 超时时间由业务决定
                latch.await(task.requiredMilliSeconds(), TIME_UNIT_MILLISECONDS);
            }
            catch (InterruptedException e)
            {
                LOGGER.error(e, "an interruption occurred.");
            }
        }
    }

    @Override
    public void postExecute()
    {
        // 执行完毕后释放锁
        this.releaseLock();
    }
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;

import org.apache.commons.collections.CollectionUtils;

/**
 * TaskGraph<br>
 * 任务有向图<br>
 * @param <T> 具体业务实例类
 * @param <R> 具体业务对应类型
 */
public abstract class TaskGraph<T extends ITimeMgr, R> implements ICreateTask<T, R>
{
    /**
     * 根节点
     */
    private Task<T> root;
    
    /**
     * "最宽"层的节点数量
     */
    private int maxLevelTaskNumber;
    
    /**
     * 构造函数<br>
     * 构建任务有向图<br>
     * 
     * @param taskTypeDepends 任务类型依赖关系
     */
    public TaskGraph(Map<R, List<R>> taskTypeDepends)
    {
        if (taskTypeDepends != null)
        {
            // 构建图
            this.root = buildGraph(taskTypeDepends);
            // 初始化为任务数量
            this.maxLevelTaskNumber = taskTypeDepends.size();
        }
    }
    
    /**
     * getToDoTasks<br>
     * 广度遍历图,按层输出任务列表,除去根节点<br>
     * 
     * @return 除根节点外的任务列表,层次访问任务顺序加入返回的任务列表
     */
    public final List<Task<T>> getToDoTasksAndUpdateMaxLevelTaskNum()
    {
        List<Task<T>> tasks = new ArrayList<Task<T>>();
        // 1、根节点为空 或者 子任务为空
        if (this.root == null || CollectionUtils.isEmpty(this.root.getChildren())) 
        {
            return tasks;
        }
        
        // 2、层次遍历任务,添加到列表中
        Queue<Task<T>> queue = new LinkedList<Task<T>>();
        queue.add(this.root);
        
        // 3、初始化最宽层的节点数量为1
        int curMaxLevelTaskNumber = 1;
        // 当前层节点数量
        int currentLevelTaskNumber = 1;
        // 下一层节点数量
        int nextLevelTaskNumber = 0;
        
        // 遍历队列
        while (!queue.isEmpty()) 
        {
            // 3.1、弹出队列第一个元素
            Task<T> curTask = queue.poll();
            // 更新当前层节点数(减1)
            currentLevelTaskNumber--;
            
            // 3.2、任务加入任务列表,头结点没有要执行的任务体,所以不加入任务列表
            if (curTask != this.root) 
            {
                // 如果某任务跨层依赖,可能该任务先于依赖的任务添加到列表中,为保证该任务最后添加到任务列表中,需要删除之前添加的任务记录
                delDupEleAndAddEleToList(curTask, tasks);
            }
            
            // 3.3、获取子任务列表,添加子任务到队列中
            List<Task<T>> children = curTask.getChildren();
            if (children.size() > 0)
            {
                // 添加子任务到队列中,任务在队列中,不重复添加
                for (Task<T> childTask : children)
                {
                    // 任务不在队列中,则加入队列,下层任务数量加1
                    if (!isTaskInContainer(childTask, queue))
                    {
                        queue.offer(childTask);
                        nextLevelTaskNumber += 1;
                    }
                }
            }
            
            // 3.4、当前层遍历完毕,更新当前、下层及最宽层节点数量
            if (currentLevelTaskNumber == 0) 
            {
                // 更新最宽层节点数量
                curMaxLevelTaskNumber = Math.max(curMaxLevelTaskNumber, nextLevelTaskNumber);
                // 更新当前层及下一层节点数量
                currentLevelTaskNumber = nextLevelTaskNumber;
                nextLevelTaskNumber = 0;
            }
        }
        
        // 更新Graph的最宽层节点数量
        this.maxLevelTaskNumber = curMaxLevelTaskNumber;
        
        return tasks;
    }
    
    // 删除列表中与要添加的目标元素重复的元素,并添加目标元素
    private void delDupEleAndAddEleToList(Task<T> task, List<Task<T>> taskList) 
    {
        // 1、使用迭代器删除与目标添加元素重复的元素
        Iterator<Task<T>> iter = taskList.iterator();
        while (iter.hasNext()) 
        {
            if (iter.next() == task) 
            {
                iter.remove();
            }
        }
        // 添加目标元素
        taskList.add(task);
    }
    
    // 任务是否已经存在于任务列表中
    private boolean isTaskInContainer(Task<T> task, Queue<Task<T>> taskContainer)
    {
        for (Task<T> curTask : taskContainer)
        {
            if (task.getTask() == curTask.getTask()) 
            {
                return true;
            }
        }
        return false;
    }
    
    /**
     * getMaxLevelTaskNumber<br>
     * 输出"最宽"层的节点数量<br>
     * 
     * @return "最宽"层的节点数量 
     */
    public final int getMaxLevelTaskNumber()
    {
        return this.maxLevelTaskNumber;
    }
    
    /**
     * buildGraph<br>
     * 根据任务类型依赖构建任务依赖图<br>
     * 
     * @param taskTypeDepends 任务类型依赖Map
     * @return Task<T> 图的根节点
     */
    private Task<T> buildGraph(Map<R, List<R>> taskTypeDepends) 
    {
        // 1、初始化需要的变量与容器
        // 1.1、节点数量
        int taskSize = taskTypeDepends.size();
        // 1.2、初始化任务实例Map
        Map<R, T> instances = new HashMap<>(taskSize);
        // 1.3、初始化根任务节点(没有挂接子任务)
        Task<T> taskRoot = buildBaseRootTask();
        // 1.4、初始化要执行的任务节点Map
        Map<R, Task<T>> tasks = new HashMap<>(taskSize);
        
        // 2、遍历任务类型依赖集合,构建依赖任务对象
        for (Entry<R, List<R>> entry : taskTypeDepends.entrySet()) 
        {
            // 2.1、获取当前任务类型
            R type = entry.getKey();
            
            // 2.2、取得当前任务实例,不存在则创建并放入容器中
            T taskInstance = getOrCreateInstance(type, instances);
            
            // 2.3、任务没有依赖则挂接到根节点下,有任务依赖则获取当前指向的任务,并完成依赖的任务的挂接
            
            // 获取当前任务类型对应的任务
            Task<T> curTask = getOrCreateTask(type, taskInstance, tasks);
            
            // 获取依赖的任务类型
            List<R> dependTypes = entry.getValue();
            
            // 2.3.1、当前任务无依赖,则把当前任务挂接到根节点即可
            if (dependTypes == null || dependTypes.isEmpty())
            {
                // 根节点下挂接第一批要执行的任务
                addChildTaskToParent(taskRoot, curTask);
            }
            // 2.3.2、当前任务有依赖,则挂接依赖的任务到当前任务下
            else 
            {
                for (R dependType : dependTypes)
                {
                    // 获取依赖的任务类型对应的T实例
                    T dependTaskInstance = getOrCreateInstance(dependType, instances);
                    // 获取依赖的任务
                    Task<T> dependTask = getOrCreateTask(dependType, dependTaskInstance, tasks);
                    // 挂接依赖的任务与当前任务
                    addChildTaskToParent(dependTask, curTask);
                }
            }
        }
        
        // 3、返回根任务节点
        return taskRoot;
    }
    
    /**
     * createTaskInstance<br>
     * 根据任务类型创建任务实例<br>
     * 
     * @param type 创建任务实例
     * @return 任务实例
     */
    @Override
    public abstract T createTaskInstance(R type);
    
    // 从实例集合中取出T实例,不存在则新建实例放入Map中
    private T getOrCreateInstance(R type, Map<R, T> instances)
    {
        T taskInstance = instances.get(type);
        if (taskInstance == null)
        {
            // 没有则创建对应任务实例
            taskInstance = createTaskInstance(type);
            // 缓存到Map中
            instances.put(type, taskInstance);
        }
        return taskInstance;
    }
    
    // 从任务集合中取出任务实例,不存在则新建任务放入Map中
    private Task<T> getOrCreateTask(R type, T taskInstance, Map<R, Task<T>> tasks)
    {
        Task<T> task = tasks.get(type);
        if (task == null)
        {
            // 没有则创建对应任务
            task = new Task<T>(taskInstance);
            // 初始化锁
            task.initLock();
            // 缓存到Map中
            tasks.put(type, task);
        }
        return task;
    }
    
    // 构建图的基础根任务节点(没有挂接子任务),标识任务的开始,根任务没有真正需要执行的任务实体
    private Task<T> buildBaseRootTask()
    {
        // 1、构建没有任务执行的空任务节点(没有挂接子任务)
        Task<T> emptyTask = new Task<T>(null);
        // 2、返回空任务节点
        return emptyTask;
    }
    
    // 添加子任务到父任务列表中, 子任务parents成员加入父任务
    private void addChildTaskToParent(Task<T> parent, Task<T> child)
    {
        // 父任务children挂接子任务
        parent.getChildren().add(child);
        // 子任务parent指向父任务
        child.getParents().add(parent);
    }
    
}

import java.util.List;
import java.util.Map;

/**
 * TaskGraph<br>
 * 任务有向图<br>
 */
public class DiffTaskGraph extends TaskGraph<IDataHolder, EnumBusinessType>
{

    public DiffTaskGraph(Map<EnumBusinessType, List<EnumBusinessType>> taskTypeDepends)
    {
        super(taskTypeDepends);
    }

    @Override
    public IDataHolder createTaskInstance(EnumBusinessType type)
    {
        return DiffDataHolderFactory.createDiffDataHolder(type);
    }
    
}

3.5、任务管理执行类

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * DiffTaskMgr<br>
 * 数据对比任务管理类<br>
 * 
 */
public class DiffTaskMgr
{
    private static final Logger LOGGER = LoggerFactory.getLog(DiffTaskMgr.class);

    /**
     * executeTasks<br>
     * 多线程执行diff任务<br>
     * 
     * @param taskTypeDepends 任务类型依赖集合
     * @param projectId 工程Id
     * @param scenarioId 场景Id
     */
    public static void executeTasks(Map<EnumBusinessType, List<EnumBusinessType>> taskTypeDepends, String projectId, String scenarioId) 
    {
        LOGGER.info("Diff tasks start");
        
        if (taskTypeDepends == null) 
        {
            LOGGER.error("Invalid task param.");
            return;
        }
        
        // 1、构建要执行的任务有向图
        DiffTaskGraph taskGraph = new DiffTaskGraph(taskTypeDepends);
        
        // 2、获取要执行的任务列表
        List<Task<IDataHolder>> toDoTasks = taskGraph.getToDoTasksAndUpdateMaxLevelTaskNum();
        int taskSize = toDoTasks.size();
        if (taskSize <= 0) 
        {
            LOGGER.error("To do tasks is empty.");
            return;
        }
        
        // 3、任务最大可并行数量
        int maxParallelNum = taskGraph.getMaxLevelTaskNumber();
        
        // 4、初始化线程池 自定义线程池
        ExecutorService executor = new ThreadPoolExecutor(maxParallelNum, maxParallelNum, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(taskSize));

        // 5、创建多线程任务执行器
        CompletionService<Void> completionService = new ExecutorCompletionService<Void>(
            executor);

        // 6、提交Diff任务
        for (Task<IDataHolder> curTask : toDoTasks)
        {
            completionService.submit(new Callable<Void>()
            {
                @Override
                public Void call() throws Exception
                {
                    // 设置上下文
                    ThreadContextContainer.setCurrPrjSceId(projectId, scenarioId);
                    // 执行diff任务
                    runDiffTask(curTask);
                    // 返回Void实例,这里返回null即可
                    return null;
                }
            });
        }

        // 7、等待所有任务完成
        try
        {
            for (int taskCount = 0; taskCount < taskSize; taskCount++)
            {
                completionService.take().get();
            }
        }
        catch (InterruptedException | ExecutionException e)
        {
            LOGGER.error(e, "Diff Task InterruptedException | ExecutionException.");
        }
        catch (Exception e)
        {
            LOGGER.error(e, "Diff Task exception.");
        }
        finally
        {
            // 8、关闭线程池
            try
            {
                executor.shutdownNow();
                executor.awaitTermination(DiffConst.TIMEOUT, DiffConst.TIME_UNIT_SECONDS);
            }
            catch (InterruptedException e)
            {
                LOGGER.error(e, "Executor service shut down fail, Exception is: ");
            }
        }
        
        LOGGER.info("All diff tasks end.");
    }

    // 执行单个diff任务
    private static void runDiffTask(Task<IDataHolder> curTask) 
    {
        // 1、等待parent任务执行完毕
        curTask.preExecute();
        
        // 2、执行diff任务
        try 
        {
            curTask.getTask().processDataDiff();
        }
        finally
        {
            // 3、设置本任务状态为完成
            curTask.postExecute();
        }
    }
    
}

3.6、对外服务类

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.collect.ImmutableList;

/**
 * DiffService WhatIf调整前后 结果对比服务类<br>
 * 
 */
@Service
public class DiffService 
{
    private static final Logger LOGGER = LoggerFactory.getLog(DiffService.class);

    /**
     * 要执行的任务类型及依赖关系
     */
    private static final Map<EnumBusinessType,List<EnumBusinessType>> BUSINESS_TYPES_DEPENDS = new HashMap<>();

    static 
    {
        BUSINESS_TYPES_DEPENDS.put(EnumBusinessType.XXX, null);
        ...
        BUSINESS_TYPES_DEPENDS.put(EnumBusinessType.LOAD, ImmutableList.of(EnumBusinessType.XXX));
        ...
    }

    private SummaryDataMgr summaryDataMgr;
    
    /**
     * processDataDiff 生成flow、tunnel、load等差异数据并入库<br>
     * 
     * @param projectId projectId
     * @param scenarioId scenarioId
     */
    public void processDataDiff(String projectId, String scenarioId)
    {
        // 1、准备工作
        doPre(projectId, scenarioId);
        
        // 2、触发diff & summary 计算、入库
        DiffTaskMgr.executeTasks(getTaskTypeDepends(), projectId, scenarioId);
        
        // 3、后续处理工作
        doPost(projectId, scenarioId);
    }
    
    private Map<EnumBusinessType, List<EnumBusinessType>> getTaskTypeDepends()
    {
        return BUSINESS_TYPES_DEPENDS;
    }
    
    private void doPre(String projectId, String scenarioId)
    {
        // 1、设置工程场景上下文
        
        // 2、清除summary数据
    }
    
    private void doPost(String projectId, String scenarioId)
    {
        // 清理缓存等
    }
    
}

参考:
任务调度系统-任务依赖的设计
数据结构-图
原型篇–有依赖的并发任务执行-代码实践(工厂、模板模式、线程池的使用)
一个著名的任务调度系统是如何设计的
spark job stage task介绍

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值