{"id":16422629,"url":"https://github.com/pizihao/crow","last_synced_at":"2025-10-26T22:31:30.402Z","repository":{"id":37739374,"uuid":"478132321","full_name":"pizihao/crow","owner":"pizihao","description":"实现多线程并行或串行处理的工具包","archived":false,"fork":false,"pushed_at":"2023-03-21T01:47:09.000Z","size":388,"stargazers_count":14,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-01T00:44:16.492Z","etag":null,"topics":["completablefuture","java","thread"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pizihao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-05T13:05:34.000Z","updated_at":"2024-09-27T02:10:28.000Z","dependencies_parsed_at":"2023-02-16T14:16:08.875Z","dependency_job_id":null,"html_url":"https://github.com/pizihao/crow","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pizihao%2Fcrow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pizihao%2Fcrow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pizihao%2Fcrow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pizihao%2Fcrow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pizihao","download_url":"https://codeload.github.com/pizihao/crow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238408435,"owners_count":19467094,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["completablefuture","java","thread"],"created_at":"2024-10-11T07:37:10.348Z","updated_at":"2025-10-26T22:31:30.033Z","avatar_url":"https://github.com/pizihao.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 介绍\n\n基于CompletableFuture对多线程串行化执行的扩展和精简，通过组合的关系避开CompletableFuture中复杂的逻辑结构，并精炼出其核心常用的功能进行扩展。简化CompletableFuture的串行化执行方式，对CompletableFuture并行化执行结果的后去方式进行扩充，并将两者进行结合，实现串行化和并行化的互相转化。\n\n\n\n快速使用\n---\n\n引入依赖：\n\n~~~xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.pizihao\u003c/groupId\u003e\n    \u003cartifactId\u003ecrow\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.8\u003c/version\u003e\n\u003c/dependency\u003e\n~~~\n\n### 串行化\n\n通过本项目进行串行化的任务执行有两种方式：\n\n串行化的执行过程如下图所示：\n\n~~~mermaid\ngraph LR\nA --\u003e B --\u003e D --\u003e E\n~~~\n\n#### 1，Multi\n\n\u003e 直接使用CompletableFuture的扩展类：Multi，使用方式如下：\n\n~~~java\n// 借助工具类MultiHelper创建Multi\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    Integer join = MultiHelper.create(executorService)\n        .thenRun(() -\u003e System.out.println(5))\n        .thenApply(unused -\u003e 41)\n        .thenApply(integer -\u003e integer - 10)\n        .join();\n    System.out.println(join);\n}\n// 结果为：\n// 5\n// 31\n~~~\n\nMulti提供的方法是对CompletableFuture精简后的方法，分别是：\n\n~~~java\n// 单个Multi的串行化执行\n\u003cU\u003e Multi\u003cU\u003e thenApply(Function\u003c? super T, ? extends U\u003e fn);\nMulti\u003cVoid\u003e thenAccept(Consumer\u003c? super T\u003e action);\nMulti\u003cVoid\u003e thenRun(Runnable action);\n// 多个Multi的聚合操作\n\u003cU, V\u003e Multi\u003cV\u003e thenCombine(Multi\u003c? extends U\u003e other, BiFunction\u003c? super T, ? super U, ? extends V\u003e fn);\n\u003cU\u003e Multi\u003cVoid\u003e thenBiAccept(Multi\u003c? extends U\u003e other, BiConsumer\u003c? super T, ? super U\u003e action);\nMulti\u003cVoid\u003e runRunBoth(Multi\u003c?\u003e other, Runnable action);\n\u003cU\u003e Multi\u003cU\u003e applyFun(Multi\u003c? extends T\u003e other, Function\u003c? super T, U\u003e fn);\nMulti\u003cVoid\u003e acceptFun(Multi\u003c? extends T\u003e other, Consumer\u003c? super T\u003e action);\nMulti\u003cVoid\u003e runFun(Multi\u003c?\u003e other, Runnable action);、\n\u003cU\u003e Multi\u003cU\u003e thenCompose(Function\u003c? super T, ? extends Multi\u003cU\u003e\u003e fn);\n// 针对异常的操作\nMulti\u003cT\u003e exceptionally(Function\u003cThrowable, ? extends T\u003e fn);\nMulti\u003cT\u003e whenComplete(BiConsumer\u003c? super T, ? super Throwable\u003e action);\n\u003cU\u003e Multi\u003cU\u003e handle(BiFunction\u003c? super T, Throwable, ? extends U\u003e fn);\n~~~\n\n#### 2，SerialMulti\n\n\u003e 通过对Multi进一步封装，就形成了SerialMulti，通过SerialMulti可以更加简便的使用Multi，如下:\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    String join = SerialMulti.of(executorService)\n        .add(() -\u003e 20)\n        .add(integer -\u003e integer + 5)\n        .add(integer -\u003e {\n            System.out.println(integer);\n        })\n        .add(() -\u003e System.out.println(10))\n        .add(() -\u003e \"测试\")\n        .join();\n    System.out.println(join);\n}\n// 结果为：\n// 25\n// 10\n// 测试\n~~~\n\nSerialMulti是对Multi的简单封装，仅仅包含有关串行化的核心方法，如thenApply，thenAccept，thenRun和exceptionally\n\n#### 3，补充(0.0.8)\n\n​\t在0.0.8版本中为串行化过程添加了任务队列和结果集，这使得整个串行化过程的每个节点的执行过程一目了然，因为不再丢弃和覆盖之前的节点，所以串行化执行过程更加可控，即可以随时回退到中间的某个过程，也可以通过任务集直接查看整个任务中某个节点的执行结果\n\n### 并行化\n\n并行化的执行过程如下图所示：\n\n~~~mermaid\ngraph LR\nA --\u003e D\nA --\u003e C\nA --\u003e B\n\n~~~\n\n上图中的A代表任务的起点，可能是一个独立的任务也可能没有任何意义，总而言之，B、C、D三个任务是完全独立的。\n\n此模式的任务可以通过ParallelMulti进行构建，如下：\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    List\u003c?\u003e list = ParallelMulti.of(executorService)\n        .add(() -\u003e 12)\n        .add(() -\u003e 50)\n        .add(() -\u003e \"测试\")\n        .resultList();\n    System.out.println(list);\n}\n// 结果：\n// [12, 50, 测试]\n~~~\n\n并行化执行的结果是按照任务装配的顺序输出的，通过这个特性可以精准的获取到对应的结果。\n\n### 复杂并行化\n\n#### 1，无交集并行化\n\n以上是对简单并行化任务的执行实例，在实际的业务流程中，一个并行化任务的子节点可能就是一个串行化的任务，如下图：\n\n~~~mermaid\ngraph LR\nA --\u003e D --\u003e E\nA --\u003e C --\u003e F\nA --\u003e B --\u003e G\n\n~~~\n\n同样，A可以是一个无意义的节点，上图中，B和G，C和F，D和E三个组之间毫无关联，但是各个组内是串行化的关系，这是一种复杂的并行化。\n\n同样可以使用ParallelMulti来实现上述模型的机制：\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    SerialMulti\u003cInteger\u003e serialMulti1 = SerialMulti.of(executorService, () -\u003e 10)\n        .add(integer -\u003e integer + 10);\n    SerialMulti\u003cInteger\u003e serialMulti2 = SerialMulti.of(executorService, () -\u003e 20)\n        .add(integer -\u003e integer + 20);\n    SerialMulti\u003cInteger\u003e serialMulti3 = SerialMulti.of(executorService, () -\u003e 30)\n        .add(integer -\u003e integer + 30);\n\n    List\u003c?\u003e objects = ParallelMulti.of(executorService)\n        .add(serialMulti1)\n        .add(serialMulti2)\n        .add(serialMulti3)\n        .resultList();\n\n    System.out.println(objects);\n\n}\n// 结果：\n// [20, 40, 60]\n~~~\n\n注意：ParallelMulti再执行时并不会保留中间过程的结果\n\n#### 2，有交集并行化\n\n~~~mermaid\ngraph LR\nA --\u003e D --\u003e E --\u003e H\nA --\u003e C --\u003e F --\u003e H\nA --\u003e B --\u003e G --\u003e H\n~~~\n\n以上仅是一个简略的模型，A同样可以看作是一个无意义的节点，三组并行执行的任务最终都汇聚向了H，H之后可能也会存在任务，此时A-\u003eH这个模型就可以看成是一个串行化结构的一个节点。这也是一种复杂的串行化模型\n\n在ParallelMulti存在交集处理的方法：\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    SerialMulti\u003cInteger\u003e serialMulti1 = SerialMulti.of(executorService, () -\u003e 10)\n        .add(integer -\u003e integer + 10);\n    SerialMulti\u003cInteger\u003e serialMulti2 = SerialMulti.of(executorService, () -\u003e 20)\n        .add(integer -\u003e integer + 20);\n    SerialMulti\u003cInteger\u003e serialMulti3 = SerialMulti.of(executorService, () -\u003e 30)\n        .add(integer -\u003e integer + 30);\n\n    ParallelMulti parallelMulti = ParallelMulti.of(executorService)\n        .add(serialMulti1)\n        .add(serialMulti2)\n        .add(serialMulti3);\n\n    parallelMulti.thenRun(() -\u003e System.out.println(\"处理完成\"));\n    parallelMulti.thenExecList((Consumer\u003cList\u003c?\u003e\u003e) System.out::println);\n    String thenExecList = parallelMulti.thenExecList(objects -\u003e {\n        System.out.println(objects);\n        return \"执行完成\";\n    });\n    System.out.println(thenExecList);\n\n}\n// 结果\n// 处理完成\n// [20, 40, 60]\n// [20, 40, 60]\n// 执行完成\n~~~\n\n#### 3，并行化补充(0.0.8)\n\n​\t在0.0.7版本，并行化的执行有很大的缺陷，即任务的装入顺序为结果的获取顺序，尽管通过类型匹配和填充可以解决一大部分结果顺序造成的代码繁琐问题但是并行化任务的执行并不应该是一个加入任务和等待结果的过程，为了使并行化的任务节点更加可控，为每个节点添加索引，添加新的任务时可以指定索引，该索引也可以由并行化过程直接生成，通过索引使得节点的结果顺序完全可见。  \n\n​\t在0.0.8版本中针对并行化的异常也引入了新的处理逻辑，即在获取结果时再将异常节点加入的执行链中，异常节点可以全局添加也可以通过索引赋值。\n\n### 任务调用顺序\n\n在由一个根节点触发的多元化串行化的执行方法中，如果存在一个较长的链状调用，并且每个任务的执行时间都较长的话，会形成一个“先来后执行”的调用结构，这不同于一般的串行化和并行化结构，其本质依然是并行化，但是和并行化有着完全不同的任务调用顺序，比如：存在这样的几个任务，A，B，C，D，E，F，G\n\n\u003e A为根节点，由A衍生出B,C,D。如下图：\n\n~~~mermaid\ngraph LR\nA --\u003e B\nA --\u003e C\nA --\u003e D\n~~~\n\n正常情况上看这是一个简单的并行化模型，在crow的简化下，这个链的执行顺序为B –\u003e C -\u003e D，并行化的结构下A只是BCD为一个组的象征，没有任何意义，当A有了实际的功能，A就成为了BCD三个任务的交点，如果按照装入顺序为B -\u003e C -\u003e D来看的话最终的调用顺序就是 A -\u003e D -\u003e C -\u003e B，这是在每个任务的执行时间都较长的情况下。而经过这样的转化就无法通过转入的索引值来获取返回结果。\n\n\u003e 如果加入E，F，G在转入顺序是这样的情况下：\n\n~~~mermaid\ngraph LR\nA --\u003e B --\u003e E\nA --\u003e C --\u003e F\nA --\u003e D --\u003e G\n~~~\n\n考虑到正常业务中的方法耗时，其调用顺序是这样的：\n\n~~~mermaid\ngraph LR\nA --\u003e D --\u003e G\nA --\u003e C --\u003e F\nA --\u003e B --\u003e E\nD --\u003e C\nC --\u003e B\n~~~\n\nA -\u003e D -\u003e G -\u003e C -\u003e F -\u003e B -\u003e E,这种情况在单纯的串行化和并行化调用中并不会存在，如果试图在一个Multi上建立分支，如上图中的A -\u003e B -\u003e E 和 A -\u003e C -\u003e F 的关系，当然 Multi是支持这种情况的，不过这样的组织结构会让代码变得更加凌乱\n\n\u003e 如果出现了上述的业务情况，可以使用ParallelMulti来代替Multi，即A和B，C，D，E，F，G 区分来看，将B，C，D，E，F，G合并为一个ParallelMult此处使用P来表示。其和无交集并行化的结构相似，这样就可以使用SerialMulti来构建模型了，其结构为：A -\u003e Z\n\n## 更多\n\n### 1，泛型支持\n\n参考：[TypeBuilder](https://github.com/ikidou/TypeBuilder)，提供了快速创建泛型的方式，包含对泛型上限和下限的控制和泛型嵌套的控制。\n\n1. 创建一个List\u003cInteger\\\u003e\n\n   ~~~java\n   Type build = TypeBuilder.make(List.class)\n       .add(Integer.class)\n       .build();\n   ~~~\n\n2. 创建一个List\u003c？super Integer\\\u003e\n\n   ~~~java\n   Type build = TypeBuilder.make(List.class)\n       .addSuper(Integer.class)\n       .build();\n   ~~~\n\n3. 创建一个Map\u003cString, Integer\\\u003e，对于多个泛型的类来说，类型被确定的顺序就是泛型的顺序，如下：String会成为key，Integer会成为value\n\n   ~~~java\n   Type build = TypeBuilder.make(Map.class)\n       .add(String.class)\n       .add(Integer.class)\n       .build();\n   ~~~\n\n4. 创建一个Map\u003cString, List\u003cInteger\\\u003e\u003e，通过一个嵌套结构控制嵌套的泛型关系\n\n   ~~~java\n   Type build = TypeBuilder.make(Map.class)\n       .add(String.class)\n       .nested(List.class)\n       .add(Integer.class)\n       .parent()\n       .build();\n   ~~~\n\n5. 创建一个String，如果不执行泛型则直接创建一个和Class兼容的类型\n\n   ~~~java\n   Type build = TypeBuilder.make(Integer.class)\n       .build();\n   ~~~\n\n### 2，类型匹配\n\n类型匹配是针对串行化的执行会出现多个结果的情况进行优化，让并行化的结果获取更加便捷\n\n#### 1，Class匹配\n\n主要针对结果不存在泛型的情况，其优点是速度快，缺点是存在局限性不支持泛型\n\n示例：\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n\n\n    String str = ParallelMulti.of(executorService)\n        .add(() -\u003e 12)\n        .add(() -\u003e \"Multi\")\n        .add(() -\u003e new Thread())\n        .get(String.class);\n\n    System.out.println(str);\n}\n// 结果\n// Multi\n~~~\n\n#### 2，Type匹配\n\n在兼容Class匹配的基础上支持对泛型的匹配，TypeBuilder被用于类型的构建\n\n示例：\n\n~~~java\npublic static void main(String[] args) {\n    ExecutorService executorService = ThreadPool.executorService();\n    List\u003cString\u003e str = ParallelMulti.of(executorService)\n        .add(() -\u003e {\n            List\u003cInteger\u003e list = new ArrayList\u003c\u003e();\n            list.add(1);\n            return list;\n        })\n        .add(() -\u003e {\n            List\u003cString\u003e list = new ArrayList\u003c\u003e();\n            list.add(\"Multi\");\n            return list;\n        })\n        .add(() -\u003e {\n            List\u003cThread\u003e list = new ArrayList\u003c\u003e();\n            list.add(new Thread());\n            return list;\n        })\n        .get(TypeBuilder.list(String.class));\n\n    System.out.println(str);\n}\n// 结果\n// [Multi]\n~~~\n\n#### 3，类型填充\n\n区别于类型匹配功能，是将一个并行化任务的结果按照类型和顺序的匹配关系直接填充到一个类中。\n\n示例：\n\n~~~java\n@Data\n@ToString\npublic class Basic {\n\n    List\u003cString\u003e strings;\n    List\u003cInteger\u003e integers;\n    List\u003cDouble\u003e doubles;\n    List\u003cFloat\u003e floats;\n    List\u003cShort\u003e shorts;\n    List\u003cByte\u003e bytes;\n    List\u003cLong\u003e longs;\n    List\u003cCharacter\u003e characters;\n    List\u003cBoolean\u003e booleans;\n\n}\n// =================================================\n\npublic static void main(String[] args) {\n\n    Basic basic = new Basic();\n    FixedMultiTools multiTools = new FixedMultiTools();\n    Basic instance = multiTools.parallelMulti()\n        .add(() -\u003e {\n            List\u003cInteger\u003e integers = new ArrayList\u003c\u003e();\n            integers.add(1);\n            integers.add(777);\n            return integers;\n        }).add(() -\u003e {\n            List\u003cString\u003e strings = new ArrayList\u003c\u003e();\n            strings.add(\"123\");\n            strings.add(\"456\");\n            return strings;\n        }).add(() -\u003e {\n            List\u003cDouble\u003e doubles = new ArrayList\u003c\u003e();\n            doubles.add(1.20001);\n            doubles.add(20.220002);\n            return doubles;\n        }).add(() -\u003e {\n            List\u003cFloat\u003e floats = new ArrayList\u003c\u003e();\n            floats.add(1.1f);\n            floats.add(1.556f);\n            return floats;\n        }).add(() -\u003e {\n            List\u003cShort\u003e shorts = new ArrayList\u003c\u003e();\n            shorts.add((short) 4);\n            shorts.add((short) 1288);\n            return shorts;\n        }).add(() -\u003e {\n            List\u003cByte\u003e bytes = new ArrayList\u003c\u003e();\n            bytes.add((byte) 125);\n            bytes.add((byte) 15);\n            return bytes;\n        }).add(() -\u003e {\n            List\u003cLong\u003e longs = new ArrayList\u003c\u003e();\n            longs.add(45L);\n            longs.add(45487L);\n            return longs;\n        }).add(() -\u003e {\n            List\u003cCharacter\u003e characters = new ArrayList\u003c\u003e();\n            characters.add('a');\n            characters.add('Z');\n            return characters;\n        }).add(() -\u003e {\n            List\u003cBoolean\u003e booleans = new ArrayList\u003c\u003e();\n            booleans.add(true);\n            booleans.add(false);\n            return booleans;\n        }).add(() -\u003e {\n            List\u003cInteger\u003e integers = new ArrayList\u003c\u003e();\n            integers.add(2);\n            integers.add(888);\n            return integers;\n        })\n        .getForInstance(basic);\n    System.out.println(instance);\n\n}\n// 结果:\n// Basic{strings=[123, 456], integers=[1, 777], doubles=[1.20001, 20.220002], floats=[1.1, 1.556], shorts=[4, 1288], bytes=[125, 15], longs=[45, 45487], characters=[a, Z], booleans=[true, false]}\n~~~\n\n### 3，类型压缩器\n\n类型压缩器用于将一个大型的对象压缩为一个完全兼容的小型的对象，从而可以更快速的进行序列化。\n\n其思路为用部分代表整体，系统中默认提供了对Iterator类型和Map类型的压缩器，使用时可自行扩展。\n\n详情可见[wiki](https://github.com/pizihao/crow/wiki)开发日志0.0.7部分。\n\n## 其他\n\nFixedMultiTools和MultiTools\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpizihao%2Fcrow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpizihao%2Fcrow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpizihao%2Fcrow/lists"}