• 【有向无环图】使用有向无环图表示任务的依赖关系


    有向无环图

    任务的执行有依赖关系,如下图所示:

    可以使用DAG(有向无环图)来维护这种依赖关系;

    定义task

    @Data
    @NoArgsConstructor
    public class NodeTask {
        private String id;
        private Set<String> dependences = Sets.newConcurrentHashSet();  //依赖的taskID
    
        public NodeTask(String id) {
            this.id = id;
        }
    
        public NodeTask addDependence(String nodeTaskId) {
            this.dependences.add(nodeTaskId);
            return this;
        }
    }
    

    Graph

    package com.ssslinppp.graph.directd;
    
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import com.google.common.collect.Queues;
    import com.google.common.graph.GraphBuilder;
    import com.google.common.graph.MutableGraph;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Queue;
    
    public class TaskGraph {
        private MutableGraph<NodeTask> taskGraph = GraphBuilder.directed().allowsSelfLoops(false).build();
    
        /**
         * 转换节点任务为Graph
         *
         * @param nodeTasks
         */
        public void parseNodeTasksToGraph(Map<String, NodeTask> nodeTasks) {
            for (NodeTask nodeTask : nodeTasks.values()) {
                if (!taskGraph.nodes().contains(nodeTask)) {
                    taskGraph.addNode(nodeTask);
                }
    
                for (String dependence : nodeTask.getDependences()) {
                    taskGraph.putEdge(nodeTask, nodeTasks.get(dependence));
                }
            }
        }
    
        /**
         * 判断是否为DAG(Directed Acyclic Graph)有向无环图
         * <p>
         * 算法思路:
         * <ul>
         * <li>1. 根据"拓扑排序"算法判断:拓扑排序之后,若还剩有点,则表示有环</li>
         * <li>2. 拓扑排序算法:找到图中所有入度为0的点,放入序列,删除这些点和以这些点为出度的边,再找所有入度为0的点,依次循环</li>
         * </ul>
         *
         * @return
         */
        public boolean isDAGraph() {
            Map<String, Integer> nodeInDegreeMap = Maps.newHashMap();
            Queue<NodeTask> queue = Queues.newArrayDeque();
            List<String> topologicalSortList = Lists.newArrayList(); //拓扑排序列表维护
    
            // 获取所有入度为0的节点
            for (NodeTask nodeTask : taskGraph.nodes()) {
                int indegree = taskGraph.inDegree(nodeTask);
                nodeInDegreeMap.put(nodeTask.getId(), indegree);
                if (indegree == 0) {
                    queue.add(nodeTask);
                    topologicalSortList.add(nodeTask.getId());
                }
            }
    
            while (!queue.isEmpty()) {
                NodeTask preNode = queue.poll(); //获取并删除
    
                for (NodeTask successorNode : taskGraph.successors(preNode)) {
                    int indegree = nodeInDegreeMap.get(successorNode.getId());
                    if (--indegree == 0) {//-1:等效删除父节点以及相应的边
                        queue.offer(successorNode); //insert
                        topologicalSortList.add(successorNode.getId());
                    }
                    nodeInDegreeMap.put(successorNode.getId(), indegree);
                }
            }
    
            System.out.println("拓扑排序(topologicalSortList):" + topologicalSortList);
    
            if (topologicalSortList.size() != taskGraph.nodes().size()) {
                return false;
            }
    
            return true;
        }
    
        /**
         * 打印Graph中task的依赖关系
         */
        public void print() {
            System.out.println("=============NodeTask count: " + taskGraph.nodes().size());
            for (NodeTask nodeTask : taskGraph.nodes()) {
                System.out.println("-------- NodeTask: " + nodeTask.getId() + "--------");
                System.out.print("Dependent on: ");
                taskGraph.successors(nodeTask).forEach((v) -> System.out.print(v.getId() + ", "));
                System.out.println();
                System.out.print("all predecessors: ");
                taskGraph.predecessors(nodeTask).forEach((v) -> System.out.print(v.getId() + ", "));
                System.out.println();
                System.out.println();
            }
        }
    }
    

    Test

    package com.ssslinppp.graph.directd;
    
    import com.google.common.collect.Maps;
    import org.junit.Test;
    
    import java.util.Map;
    
    public class TaskGraphTest {
        /**
         * 测试依赖关系
         */
        @Test
        public void testDependence() {
            Map<String, NodeTask> nodeTaskMap = Maps.newConcurrentMap();
            NodeTask nodeTaskA = new NodeTask("nodeTaskA");
            NodeTask nodeTaskB = new NodeTask("nodeTaskB");
            NodeTask nodeTaskC = new NodeTask("nodeTaskC").addDependence("nodeTaskA");
            NodeTask nodeTaskD = new NodeTask("nodeTaskD").addDependence("nodeTaskB");
            NodeTask nodeTaskE = new NodeTask("nodeTaskE").addDependence("nodeTaskC").addDependence("nodeTaskD");
            NodeTask nodeTaskF = new NodeTask("nodeTaskF").addDependence("nodeTaskE");
            NodeTask nodeTaskG = new NodeTask("nodeTaskG").addDependence("nodeTaskE");
            nodeTaskMap.put(nodeTaskA.getId(), nodeTaskA);
            nodeTaskMap.put(nodeTaskB.getId(), nodeTaskB);
            nodeTaskMap.put(nodeTaskC.getId(), nodeTaskC);
            nodeTaskMap.put(nodeTaskD.getId(), nodeTaskD);
            nodeTaskMap.put(nodeTaskE.getId(), nodeTaskE);
            nodeTaskMap.put(nodeTaskF.getId(), nodeTaskF);
            nodeTaskMap.put(nodeTaskG.getId(), nodeTaskG);
    
            TaskGraph taskGraph = new TaskGraph();
            taskGraph.parseNodeTasksToGraph(nodeTaskMap);
            System.out.println("======== DAG(有向无环图)判断 ===========");
            if (taskGraph.isDAGraph()) {
                System.out.println("is DAG");
            } else {
                System.out.println("Not DAG");
            }
    
            System.out.println("=============== print ===========");
            taskGraph.print();
        }
    
    
        /**
         * 判断是否为有向无环图
         */
        @Test
        public void testDAG() {
            // E依赖D, D依赖B, B依赖E ==>(B,E,G)为一个环
            Map<String, NodeTask> nodeTaskMap = Maps.newConcurrentMap();
            NodeTask nodeTaskA = new NodeTask("nodeTaskA");
            NodeTask nodeTaskB = new NodeTask("nodeTaskB");
    //        nodeTaskB.addDependence("nodeTaskE");  // 在这里控制是否有环,进行测试
            NodeTask nodeTaskC = new NodeTask("nodeTaskC").addDependence("nodeTaskA");
            NodeTask nodeTaskD = new NodeTask("nodeTaskD").addDependence("nodeTaskB");
            NodeTask nodeTaskE = new NodeTask("nodeTaskE").addDependence("nodeTaskC").addDependence("nodeTaskD");
            NodeTask nodeTaskF = new NodeTask("nodeTaskF").addDependence("nodeTaskE");
            NodeTask nodeTaskG = new NodeTask("nodeTaskG").addDependence("nodeTaskE");
            nodeTaskMap.put(nodeTaskA.getId(), nodeTaskA);
            nodeTaskMap.put(nodeTaskB.getId(), nodeTaskB);
            nodeTaskMap.put(nodeTaskC.getId(), nodeTaskC);
            nodeTaskMap.put(nodeTaskD.getId(), nodeTaskD);
            nodeTaskMap.put(nodeTaskE.getId(), nodeTaskE);
            nodeTaskMap.put(nodeTaskF.getId(), nodeTaskF);
            nodeTaskMap.put(nodeTaskG.getId(), nodeTaskG);
    
            TaskGraph taskGraph = new TaskGraph();
            taskGraph.parseNodeTasksToGraph(nodeTaskMap);
            System.out.println("======== DAG(有向无环图)判断 ===========");
            if (taskGraph.isDAGraph()) {
                System.out.println("It is DAG");
            } else {
                System.out.println("Not DAG");
            }
        }
    }
    

    pom

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>23.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

    源码

    源码Github

  • 相关阅读:
    深入学习Spring框架(四)- 事务管理
    深入学习Spring框架(三)- AOP面向切面
    深入学习Spring框架(二)- 注解配置
    深入学习Spring框架(一)- 入门
    深入学习Mybatis框架(二)- 进阶
    深入学习Mybatis框架(一)- 入门
    短uuid生成
    from __future__ import division
    django-orm 快速清理migrations缓存
    Ajax请求设置csrf_token
  • 原文地址:https://www.cnblogs.com/ssslinppp/p/8671378.html
Copyright © 2020-2023  润新知