From 980dc41f022296fb75f1124d81cbeadd9b065aec Mon Sep 17 00:00:00 2001 From: TcSnZh Date: Wed, 19 May 2021 22:53:43 +0800 Subject: [PATCH 1/5] =?UTF-8?q?v1.5.3=20=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=88=9D=E6=AD=A5=E6=9E=84=E6=80=9D?= =?UTF-8?q?=E4=BA=86=E5=8A=A8=E6=80=81=E4=BB=BB=E5=8A=A1=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 40 +- QuickStart.md | 634 +++++++++++++++--- README.md | 13 +- asyncTool-core/pom.xml | 27 + .../async/callback/DefaultCallback.java | 8 +- .../async/callback/DefaultGroupCallback.java | 0 .../jd/platform/async/callback/ICallback.java | 20 +- .../async/callback/IGroupCallback.java | 0 .../async/callback/ITimeoutWorker.java | 0 .../jd/platform/async/callback/IWorker.java | 6 +- .../async/exception/SkippedException.java | 0 .../com/jd/platform/async/executor/Async.java | 15 +- .../async/executor/PollingCenter.java | 14 +- .../async/executor/timer/SystemClock.java | 1 - .../jd/platform/async/worker/ResultState.java | 0 .../jd/platform/async/worker/WorkResult.java | 2 - .../wrapper/QuickBuildWorkerWrapper.java | 100 +++ .../async/wrapper/StableWorkerWrapper.java | 0 .../wrapper/StableWorkerWrapperBuilder.java | 39 +- .../platform/async/wrapper/WorkerWrapper.java | 186 +++-- .../async/wrapper/WorkerWrapperBuilder.java | 14 +- .../async/wrapper/WorkerWrapperGroup.java | 9 +- .../wrapper/strategy/WrapperStrategy.java | 133 ++++ .../depend/DependMustStrategyMapper.java | 2 +- .../depend/DependOnUpWrapperStrategy.java | 10 +- .../DependOnUpWrapperStrategyMapper.java | 13 +- .../strategy/depend/DependenceAction.java | 0 .../strategy/depend/DependenceStrategy.java | 16 +- .../wrapper/strategy/skip/SkipStrategy.java | 2 +- .../test/java/beforev14/depend/DeWorker.java | 0 .../test/java/beforev14/depend/DeWorker1.java | 0 .../test/java/beforev14/depend/DeWorker2.java | 0 .../src}/test/java/beforev14/depend/Test.java | 0 .../src}/test/java/beforev14/depend/User.java | 0 .../java/beforev14/dependnew/DeWorker.java | 0 .../java/beforev14/dependnew/DeWorker1.java | 0 .../java/beforev14/dependnew/DeWorker2.java | 0 .../test/java/beforev14/dependnew/Test.java | 0 .../test/java/beforev14/dependnew/User.java | 0 .../beforev14/parallel/ParTimeoutWorker.java | 0 .../java/beforev14/parallel/ParWorker.java | 0 .../java/beforev14/parallel/ParWorker1.java | 0 .../java/beforev14/parallel/ParWorker2.java | 0 .../java/beforev14/parallel/ParWorker3.java | 0 .../java/beforev14/parallel/ParWorker4.java | 0 .../test/java/beforev14/parallel/TestPar.java | 0 .../java/beforev14/seq/SeqTimeoutWorker.java | 0 .../test/java/beforev14/seq/SeqWorker.java | 0 .../test/java/beforev14/seq/SeqWorker1.java | 0 .../test/java/beforev14/seq/SeqWorker2.java | 0 .../java/beforev14/seq/TestSequential.java | 0 .../beforev14/seq/TestSequentialTimeout.java | 0 .../src}/test/java/v15/cases/Case1.java | 0 .../src}/test/java/v15/cases/Case2.java | 0 .../src}/test/java/v15/cases/Case3.java | 2 + .../src}/test/java/v15/cases/Case4.java | 0 .../src}/test/java/v15/cases/Case5.java | 0 .../src}/test/java/v15/cases/Case6.java | 5 - .../src}/test/java/v15/cases/Case7.java | 0 .../src}/test/java/v15/cases/Case8.java | 1 + .../src/test/java/v15/cases/Case9.java | 59 ++ .../src}/test/java/v15/wrappertest/Test.java | 0 asyncTool-openutil/pom.xml | 19 + .../com/jd/platform/async/openutil/BiInt.java | 109 +++ .../openutil/collection/AbstractArray2D.java | 74 ++ .../collection/AbstractDirectedGraph.java | 88 +++ .../openutil/collection/AbstractStoreArk.java | 22 + .../async/openutil/collection/Array2D.java | 169 +++++ .../async/openutil/collection/BiTree.java | 76 +++ .../openutil/collection/CachedStoreArk.java | 60 ++ .../collection/CollisionRangeTable.java | 10 + .../collection/CommonDirectedGraph.java | 145 ++++ .../openutil/collection/CommonStoreArk.java | 159 +++++ .../openutil/collection/DirectedGraph.java | 185 +++++ .../async/openutil/collection/Graph.java | 106 +++ .../openutil/collection/SparseArray2D.java | 230 +++++++ .../async/openutil/collection/StoreArk.java | 69 ++ .../async/openutil/collection/Tree.java | 16 + .../openutil}/collection/WheelIterator.java | 2 +- .../async/openutil/concurrent/RangeLock.java | 46 ++ .../openutil}/timer/AbstractWheelTimer.java | 3 +- .../openutil}/timer/HashedWheelTimer.java | 46 +- .../async/openutil}/timer/Timeout.java | 3 +- .../platform/async/openutil}/timer/Timer.java | 6 +- .../async/openutil}/timer/TimerTask.java | 2 +- .../async/openutil/unsafe/UnsafeUtil.java | 33 + .../test/java/openutiltest/PrintProxy.java | 51 ++ .../src/test/java/openutiltest/TestGraph.java | 37 + asyncTool-scheduling/pom.xml | 31 + .../DefaultSchedulingJsonParser.java | 40 ++ .../async/scheduling/SchedulingFactory.java | 35 + .../scheduling/SchedulingJsonParser.java | 24 + .../exception/IllegalConfigException.java | 25 + .../async/scheduling/model/ObjectModel.java | 80 +++ .../model/SchedulingDrawingsModel.java | 255 +++++++ .../async/scheduling/util/ReflectUtil.java | 16 + .../java/schedulingtest/FileStringReader.java | 41 ++ .../test/java/schedulingtest/PrintParam.java | 17 + .../test/java/schedulingtest/cases/Case1.java | 14 + .../test/java/schedulingtest/entity/User.java | 52 ++ .../java/schedulingtest/impl/PayTaxes.java | 37 + .../schedulingtest/impl/SelectUserByName.java | 50 ++ .../src/test/resources/case1_1.json | 61 ++ .../src/test/resources/test.json | 9 + pom.xml | 8 +- 105 files changed, 3599 insertions(+), 333 deletions(-) create mode 100644 asyncTool-core/pom.xml rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/DefaultCallback.java (66%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/ICallback.java (48%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/IGroupCallback.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/ITimeoutWorker.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/IWorker.java (91%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/exception/SkippedException.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/executor/Async.java (95%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/executor/PollingCenter.java (85%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/executor/timer/SystemClock.java (97%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/worker/ResultState.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/worker/WorkResult.java (95%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java (90%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java (81%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java (93%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java (95%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java (97%) rename src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java (87%) rename src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java (77%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java (95%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker1.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker2.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/Test.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/User.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker1.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker2.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/Test.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/User.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParTimeoutWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker1.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker2.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker3.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker4.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/TestPar.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqTimeoutWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker1.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker2.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/TestSequential.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/TestSequentialTimeout.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case1.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case2.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case3.java (96%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case4.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case5.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case6.java (89%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case7.java (100%) rename {src => asyncTool-core/src}/test/java/v15/cases/Case8.java (99%) create mode 100644 asyncTool-core/src/test/java/v15/cases/Case9.java rename {src => asyncTool-core/src}/test/java/v15/wrappertest/Test.java (100%) create mode 100644 asyncTool-openutil/pom.xml create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/collection/WheelIterator.java (87%) create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/timer/AbstractWheelTimer.java (82%) rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/timer/HashedWheelTimer.java (93%) rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/timer/Timeout.java (91%) rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/timer/Timer.java (87%) rename {src/main/java/com/jd/platform/async/util => asyncTool-openutil/src/main/java/com/jd/platform/async/openutil}/timer/TimerTask.java (79%) create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java create mode 100644 asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java create mode 100644 asyncTool-openutil/src/test/java/openutiltest/TestGraph.java create mode 100644 asyncTool-scheduling/pom.xml create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java create mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java create mode 100644 asyncTool-scheduling/src/test/resources/case1_1.json create mode 100644 asyncTool-scheduling/src/test/resources/test.json diff --git a/.gitignore b/.gitignore index 66d02f6..991259a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,32 @@ -HELP.md -target/ +**/HELP.md +**/target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/** !**/src/test/** ### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache +**/.apt_generated +**/.classpath +**/.factorypath +**/.project +**/.settings +**/.springBeans +**/.sts4-cache ### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr +**/.idea +**/*.iws +**/*.iml +**/*.ipr ### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ +**/nbproject/private/ +**/nbbuild/ +**/dist/ +**/nbdist/ +**/.nb-gradle/ +**/build/ ### VS Code ### -.vscode/ +**/.vscode/ diff --git a/QuickStart.md b/QuickStart.md index 125a6d9..a475d5a 100644 --- a/QuickStart.md +++ b/QuickStart.md @@ -4,6 +4,8 @@ 代码不多,直接拷贝包过去即可。 +#### 旧稳定版本v1.4 + 京东同事通过引用如下maven来使用。 ```xml @@ -34,11 +36,55 @@ ``` -# 使用说明 +#### 最新版本v1.5(不稳定) + + + +从gitee上下载仓库到本地,切换到`dev`分支,然后maven安装到本地仓库。 + +```bash +git clone https://gitee.com/jd-platform-opensource/asyncTool.git +cd ./asyncTool +git checkout dev +mvn install +``` + +在项目中引入依赖。 + +```xml + + + asyncTool-core + com.jd.platform + 1.5.0-SNAPSHOT + + + + asyncTool-scheduling + com.jd.platform + 1.5.0-SNAPSHOT + +``` + +# 任务编排 + +> `asyncTool-core`核心模块提供了核心功能——任务编排 +> +> 以下文档基于版本: +> +> ```xml +> +> +> com.jd.platform +> asyncTool-core +> 1.5.0-SNAPSHOT +> +> +> ``` ### 基本组件 -worker: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。 +`IWorker`: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。 T,V两个泛型,分别是入参和出参类型。 @@ -63,7 +109,7 @@ public interface IWorker { V action(T object, Map allWrappers); /** - * 超时、异常时,返回的默认值 + * 超时、异常、跳过时,返回的默认值 * * @return 默认值 */ @@ -73,8 +119,7 @@ public interface IWorker { } ``` - -callBack:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。 +`ICallback`:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。 ```java /** @@ -117,6 +162,96 @@ WorkerWrapper w0 = WorkerWrapper.builder() 通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。 +### 如何构造WorkerWrapper? + +##### 推荐Builder模式 + +如果刚开始使用这个框架,则推荐使用如下方式进行构造: + +```java +WorkerWrapper.builder() + .id() + // 其他属性略。 + // 请在《简单示例》与《设置WorkerWrapper属性》中慢慢感受详细内容。 + // 因为这里地方小,写不下。 +``` + +##### 复杂的快速构造 + +> 不推荐新手使用。 +> +> 不推荐在业务中使用,使用Builder模式代码更加简洁,且会检查参数,不必节省这些性能。 +> +> 该对象的构造方法不会检查属性。 + +在对WorkerWrapper属性有充足了解后,可使用“直接设置属性 + 关系图”的方式快速构造wrapper。 + +建议在扩展功能的时候使用该构造器,以提高效率。但是请记得检查参数。 + +以下为示例: + +```java +package v15.cases; + +import com.jd.platform.async.callback.DefaultCallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.openutil.collection.CommonDirectedGraph; +import com.jd.platform.async.openutil.collection.DirectedGraph; +import com.jd.platform.async.wrapper.QuickBuildWorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.strategy.WrapperStrategy; + +import java.util.concurrent.*; + +/** + * 快速构造示例。 + * + * @author create by TcSnZh on 2021/5/17-下午5:23 + */ +class Case9 { + public static void main(String[] args) throws ExecutionException, InterruptedException { + DirectedGraph, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>()); + QuickBuildWorkerWrapper w1 = new QuickBuildWorkerWrapper<>("id1", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 1"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + QuickBuildWorkerWrapper w2 = new QuickBuildWorkerWrapper<>("id2", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 2"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + graph.addNode(w1, w2); + graph.putRelation(w1, new Object(), w2); + +// System.out.println(graph); + + Async.beginWork(200, w1); + + System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); + + } +} +``` + ### 简单示例 1. 3个任务并行 @@ -353,9 +488,9 @@ public static ThreadPoolExecutor getCommonPool(); /** * @param now 是否立即关闭 - * @throws IllegalStateException 如果尚未调用过{@link #getCommonPool()},即没有使用过“使用默认线程池”的方法,该方法会抛出空指针异常。 + * @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。 */ -public static synchronized void shutDownCommonPool(boolean now); +public static synchronized boolean shutDownCommonPool(boolean now); ``` 以下是一个使用自定义线程池的简单代码示例: @@ -364,8 +499,6 @@ public static synchronized void shutDownCommonPool(boolean now); Async.beginWork(1000, Executors.newFixedThreadPool(2),a); ``` - - ## WorkerWrapper基本属性 ### 执行流程 @@ -381,9 +514,39 @@ WorkerWrapper会在这些情况被运行: > processOn流程图文件放在同仓库。 -### 其他属性 +### 属性 + +#### id + +`WorkerWrapper`的id属性非常重要。 + +可在builder的该属性设置id,如果不设置,默认使用UUID。 + +```java +public interface WorkerWrapperBuilder { + /** + * 设置唯一id。 + * 如果不设置,{@link StableWorkerWrapperBuilder}会使用UUID + */ + WorkerWrapperBuilder id(String id); + + // 略 +} +``` + +例如如果你需要在`IWorker`中调用上游wrapper,则可以根据id来获取到。 + +> 该map的键即为`WorkerWrapper`的id。 + +```java +V action(T object, Map> allWrappers); +``` + +请程序员确保在一次任务执行的一组wrapper中,id不会重复。在执行过程中不会进行检查。 +#### 其他省略 +> 其他属性都写在源码注释中,可下载源码慢慢查看。 ## 设置WorkerWrapper属性 @@ -466,6 +629,8 @@ class Case3 { wrapper(id=B2) is working wrapper(id=C2) is working wrapper(id=C1) is working + wrapper(id=B4) is working + // 我们看到B5被跳过了,没有执行callback */ } } @@ -531,62 +696,76 @@ public enum DependenceAction { | `FAST_FAIL` | 立即失败。WorkerWrapper会去执行快速失败的方法。 | | `JUDGE_BY_AFTER` | 交给下层`{@link DependenceStrategy}`进行判断。 由于`{@link DependenceStrategy#thenJudge(DependenceStrategy)}`的责任链设计模式,该返回值的意义就是调用责任链上下一个策略。 | +> 如果wrapper被跳过,ResultState将为`DEFAULT`。 +> +> + ##### 策略器组件默认实现 * `DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS`,该值为默认值,若builder未设置则默认使用这个。 1. 被依赖的所有Wrapper都必须成功才能开始工作。 2. 如果其中任一Wrapper还没有执行且不存在失败,则休息。 - 3. 如果其中任一Wrapper失败则立即失败。 + 3. 如果其中任一Wrapper失败则立即失败。*(跳过不算失败)* * `DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS` 1. 被依赖的Wrapper中任意一个成功了就可以开始工作。 2. 如果其中所有Wrapper还没有执行,则休息。 - 3. 如果其中一个Wrapper失败且不存在成功则立即失败。 + 3. 如果其中一个Wrapper失败且不存在成功则立即失败。*(跳过不算失败)* * `DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED` - * 如果被依赖的工作中任一失败,则立即失败。否则就开始工作(不论之前的工作有没有开始)。 + * 如果被依赖的工作中任一失败,则立即失败。*(跳过不算失败)* + * 否则就开始工作(不论之前的工作有没有开始)。 * `DependenceStrategy.theseWrapperAllSuccess(Set>)` * 该方法传入一个`Set`指定wrapper,只有当指定的这些Wrapper都成功时,才会开始工作。任一失败会快速失败。任一还没有执行且不存在失败,则休息。 * 不建议使用:~~`DependenceStrategy.IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY`~~ * 此值用于适配v1.4及之前的must开关模式,当`wrapperStrategy`的`dependMustStrategyMapper`的`mustDependSet`不为空时,则休息(因为能判断到这个责任链说明set中存在不满足的值)。为空时,则任一成功则执行。 -##### `WorkerWrapper`的三层策略器责任链 +##### `WorkerWrapper`的策略器责任链 -`WorkerWrapper`在判断时,并不是只使用一个策略进行判断的,而是在`WorkerWrapper.WrapperStrategy`进行了最多三层的判断: +`WorkerWrapper`在判断时,并不是只使用一个策略进行判断的,而是在`WrapperStrategy`进行了最多三层的判断: ```java -public abstract class WorkerWrapper { - - // 略 - - public static class WrapperStrategy implements DependenceStrategy, SkipStrategy { - // ========== 这三个策略器用于链式判断是否要开始工作 ========== +public interface WrapperStrategy extends DependenceStrategy, SkipStrategy { + // ========== 这三个策略器用于链式判断是否要开始工作 ========== - // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy + // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy - /** - * 对特殊Wrapper专用的依赖响应策略。 - * 该值允许为null - */ - private DependWrapperStrategyMapper dependWrapperStrategyMapper; - /** - * 对必须完成的(must的)Wrapper的依赖响应策略。 - * 该值允许为null - *

- * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 - */ - private DependMustStrategyMapper dependMustStrategyMapper; - /** - * 底层全局策略。 - */ - private DependenceStrategy dependenceStrategy; - - // 略 - } + /** + * 设置对特殊Wrapper专用的依赖响应策略。 + * + * @return 该值允许为null + */ + DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper(); + + /** + * 对必须完成的(must的)Wrapper的依赖响应策略。 + * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 + * + * @return 该值允许为null + */ + DependMustStrategyMapper getDependMustStrategyMapper(); + + /** + * 底层全局策略。 + * + * @return 该值不允许为null + */ + DependenceStrategy getDependenceStrategy(); + + // ========== 这是跳过策略 ========== + + /** + * 跳过策略 + * + * @return 不允许为null + */ + SkipStrategy getSkipStrategy(); + + // 其他属性略,自行查看源码即可 } ``` -正如注释所言,三个策略器将依次调用`DependenceStrategy.judgeAction(Set,WorkerWrapper,WorkerWrapper)`方法进行判断,每次判断会返回`DependenceAction.WithProperty`类型。 +正如注释所言,三个策略器将依次调用`judgeAction(Set,WorkerWrapper,WorkerWrapper)`方法进行判断,每次判断会返回`DependenceAction.WithProperty`类型。 -前两个策略器的返回值,即`DependenceAction.WithProperty`类型,若调用`getDependenceAction()`方法返回的枚举值不为`JUDGE_BY_AFTER`时,整个三层责任链将返回此返回值;若为`JUDGE_BY_AFTER`,则交给下个策略器进行判断。该方法具体由以下方法实现: +前两个策略器的返回值若不为枚举`JUDGE_BY_AFTER`的内部类时,整个三层责任链将返回此返回值;若为`JUDGE_BY_AFTER`,则交给下个策略器进行判断。该方法具体由以下方法实现: ```java public interface DependenceStrategy { @@ -788,74 +967,51 @@ class Case5 { 那么,能不能让上游wrapper根据自己的状态独自决定下游wrapper响应呢? -因此,三层策略器的`DependWrapperStrategyMapper`便是用于设置此功能的。 +可以。`DependOnUpWrapperStrategy`函数式接口 与 `DependOnUpWrapperStrategyMapper`这两个类即可完成这个功能。 ```java -// 示例版本v1.5 - /** - * 对不同的{@link WorkerWrapper}调用者实行个性化依赖响应策略。 - *

- * 使用{@link DependWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强, + * 由上游wrapper决定本wrapper行为的单参数策略。 * - * @author create by TcSnZh on 2021/5/1-下午11:12 + * @author create by TcSnZh on 2021/5/1-下午11:16 */ -public class DependWrapperStrategyMapper implements DependenceStrategy { - private final Map, DependWrapperActionStrategy> mapper = new ConcurrentHashMap<>(4); - +@FunctionalInterface +public interface DependOnUpWrapperStrategy { /** - * 设置对应策略 + * 仅使用一个参数(即调用自身的上游wrapper)的判断方法 * - * @param targetWrapper 要设置策略的WorkerWrapper - * @param strategy 要设置的策略 - * @return 返回this,链式调用。 + * @param fromWrapper 调用本Wrapper的上游Wrapper + * @return 返回 {@link DependenceAction.WithProperty} */ - public DependWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependWrapperActionStrategy strategy) {/* 略 */} + DependenceAction.WithProperty judge(WorkerWrapper fromWrapper); + + // ========== 送几个供链式调用的默认值 ========== /** - * 判断方法。 - *

- * 如果fromWrapper在{@link #mapper}中,则返回{@link DependWrapperActionStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER} - * - * @param dependWrappers (这里不会使用该值)thisWrapper.dependWrappers的属性值。 - * @param thisWrapper (这里不会使用该值)thisWrapper,即为“被催促”的WorkerWrapper - * @param fromWrapper 调用来源Wrapper。 - * @return 如果在mapper中有对fromWrapper的处理策略,则使用其进行判断。否则返回JUDGE_BY_AFTER交给下一个进行判断。 + * 成功时,交给下一个策略器判断。 + * 未运行时,休息。 + * 失败时,失败。 */ - @Override - public DependenceAction.WithProperty judgeAction( - Set> dependWrappers, - WorkerWrapper thisWrapper, - WorkerWrapper fromWrapper // 仅判断该属性 - ) {/* 略 */} - - // 略 + DependOnUpWrapperStrategy SUCCESS_CONTINUE = /*略*/ ; + /** + * 成功时,开始工作。 + * 未运行时,交给下一个策略器判断。 + * 失败时,失败。 + */ + DependOnUpWrapperStrategy SUCCESS_START_INIT_CONTINUE = /*略*/ ; } ``` -其`mapper`属性中,每个`WorkerWrapper`对应了一个`DependWrapperActionStrategy`,这个接口便是用于让上游wrapper决定下游响应的: +在`DependOnUpWrapperStrategyMapper`的`mapper`属性中,每个`WorkerWrapper`对应了一个`DependOnUpWrapperStrategy`,实现了让wrapper对不同的上游做出不同的响应策略。 ```java -@FunctionalInterface -public interface DependWrapperActionStrategy { - /** - * 仅使用一个参数的判断方法 - * - * @param fromWrapper 调用本Wrapper的上游Wrapper - * @return 返回 {@link DependenceAction.WithProperty} - */ - DependenceAction.WithProperty judge(WorkerWrapper fromWrapper); - - // 常量略 +public class DependOnUpWrapperStrategyMapper implements DependenceStrategy { + private final Map, DependOnUpWrapperStrategy> mapper = new ConcurrentHashMap<>(4); + // 以下略 } ``` -###### 提供常量 - -* `DependWrapperActionStrategy.SUCCESS_CONTINUE` - * 成功时,交给下一个策略器判断。未运行时,休息。失败时,失败。 -* `DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE` - * 成功时,开始工作。未运行时,交给下一个策略器判断。失败时,失败。 +在《`WorkerWrapper`的三层策略器责任链》这一章中,我们可以看到,第一层策略器就是此`DependOnUpWrapperStrategyMapper`。 ###### 简单使用与示例 @@ -932,7 +1088,12 @@ class Case6 { } ``` +###### 提供常量 +* `DependWrapperActionStrategy.SUCCESS_CONTINUE` + * 成功时,交给下一个策略器判断。未运行时,休息。失败时,失败。 +* `DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE` + * 成功时,开始工作。未运行时,交给下一个策略器判断。失败时,失败。 ### 设置跳过策略 @@ -943,8 +1104,9 @@ class Case6 { ```json { result: null, - resultState: ResultState.EXCEPTION, - ex: com.jd.platform.async.exception.SkippedException + // 注意:如果wrapper被跳过,ResultState将为DEFAULT + resultState: "ResultState.DEFAULT", + ex: "com.jd.platform.async.exception.SkippedException" } ``` @@ -1151,14 +1313,13 @@ class Case8 { wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) + ... 以下异常信息省略 */ } } ``` - - ### 设置是否允许被打断线程 可通过该选项去设置允许线程被打断: @@ -1178,6 +1339,8 @@ public interface WorkerWrapperBuilder { } ``` +#### 线程会被打断的具体情况 + 开启之后,在以下情况,会试图打断正处于WORKING状态的工作线程。 * 总任务超时,但本wrapper在WORKING。 @@ -1186,7 +1349,284 @@ public interface WorkerWrapperBuilder { * 调用`WorkerWrapper#failNow()`方法,且wrapper在WORKING状态。 -### +# 开放工具类 + +> `asyncTool-openutil`工具模块提供了一些便于开发的工具类。 +> +> 可单独引入依赖: +> +> ```xml +> +> +> com.jd.platform +> asyncTool-openutil +> 1.5.0-SNAPSHOT +> +> +> ``` + +### 集合类 + +> `com.jd.platform.async.openutil.collection.*` + +这里不详述,要用的话源码里有注释。 + +* `SparseArray2D` 稀疏矩阵。 +* `CommonDirectedGraph` 有向图。 +* `CommonStoreArk` id储物柜。 + +### 定时器 + +> `com.jd.platform.async.openutil.timer.*` + +* `HashedWheelTimer` 从netty里抄来的时间轮工具类。 + +### 其他 + +`com.jd.platform.async.openutil` + +* `BiInt` 一个表示两个int值的实体类,内含默认比较器、缓冲区间。 + +# 动态任务调度 + +>引入依赖 +> +>```xml +> +> +> asyncTool-scheduling +> com.jd.platform +> 1.5.0-SNAPSHOT +> +>``` + +动态任务调度,传个json就能让调度工厂随心所欲的构造wrapper与执行。 + +如果您已经熟悉**《任务编排》**这一章的内容,则可以试试这玩意。 + +## 基本组件 + +## 模型属性 + +### 字段属性详解 + +以下是一个传入的json的格式示例,注释中描述了属性含义与格式: + +```json +{ + // 图纸名称。可使用 字符串、null、不写。为null或不写,则会使用uuid + drawingsName :"", + // 提供的全部WorkerWrapper列表。 + // 内部格式请参考《wrappers》这章。 + wrappers:[], + // wrapper顺序关系。 + // 内部格式请参考《relations》这章 + relations: [], + // 任务启动参数 + // 内部格式请参考《beginWork》这章 + beginWork:{} +} +``` + +#### wrappers + +wrappers数组中,只允许存入满足以下格式的对象: + +```json +wrappers:[ + { + // id,传入字符串。 + // 而且:不能与wrappers数组中其他的对象的id属性相同。即必须保证id唯一。 + // 不允许undefined,不允许null。 + "id": "first", + // param,即参数。请看《param属性格式》 + // useObjectModel代表value的值是否是“对象模型”。 + // 允许undefined,如果undefined则使用null。显然,允许null。 + "param": { + "useObjectModel": false, + "value": "JackMa" + }, + // 传入对象模型,请看《对象模型》 + // 不允许undefined或null + "worker": { + "sameObjectId": 1, + "className:": "schedulingtest.impl.SelectUserByName" + }, + // 传入对象模型 + // 允许undefined与null。如果为两者则使用com.jd.platform.async.callback.DefaultCallback + "callback": { + "sameObjectId": 1 + }, + // wrapper依赖策略 + // 允许undefined与null。如果为两者则使用com.jd.platform.async.wrapper.strategy.WrapperStrategy.DefaultWrapperStrategy + "wrapperStrategy": { + // 传入{}键值对,键名为即wrapper的id属性,值为对象模型。 + // 允许undefined和null,两者之意与空键值对{}并无二致 + "dependOnUpWrapperStrategyMapper": null, + // -- 这里不再向历史妥协,舍弃了DependMustStrategyMapper + // 基础策略器,传入对象模型 + // 允许undefined和null,如果是两者则使用"ALL_DEPENDENCIES_ALL_SUCCESS" + "dependenceStrategy": { + "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" + }, + // 跳过策略,传入对象模型 + // 允许undefined和null,如果是两者则使用"CHECK_ONE_LEVEL" + "skipStrategy": { + "constObjectName": "CHECK_ONE_LEVEL" + } + }, + // 是否允许打断,传入boolean值。允许undefined和null,视为false + "allowInterrupt": true, + // 是否启动单wrapper计时,允许undefined和null,视为false + "enableTimeout": true, + // 单wrapper超时时间数值。传入long(int64)值 + // 在enableTimeout为true的情况下不允许为undefined或null或小于等于0的值,否则允许任何值 + "timeoutLength": 50, + // 单wrapper超时时间单位,有以下几个取值:(即java.util.concurrent.TimeUnit的枚举值) + // "NANOSECONDS"、"MICROSECONDS"、"MILLISECONDS" + // "SECONDS"、"MINUTES"、"HOURS"、"DAYS" + // 允许为undefined和null,视为"MILLISECONDS" + // 但是除了undefined、null和以上7个值外,不允许任何值 + "timeoutUnit": "MILLISECONDS" + }, + // 这是第二个wrapper属性,这里可以省略很多选用默认值的属性。 + { + "id": "second", + "param":{ + "useObjectModel": false, + "value":"first" + }, + "worker": { + "className": "schedulingtest.impl.PayTaxes", + "sameObjectId": 2 + }, + callback:{ + "sameObjectId": 2 + } + }, + // 这是第三个属性,这里的"extendConfig"属性是个省力的好东西。 + { + "id": "third", + // 传入存在的id属性 + // 效果是:本wrapper配置将继承此id表示的wrapper配置的所有属性 + // 并且本wrapper可以有选择的覆写配置——只需设置某个对象的值即可。 + // 可以为null或undefined,表示不继承配置 + "extendConfig": "second" + }, + /* , { ... } */ +] +``` + +##### param + +`useObjectModel`属性用于说明`value`属性的所代表的对象类型: + +* 为false:使用json所对应的类型。 +* 为true:使用《实现类对象规范》中的我们自定义的对象模型。 + +```json +{ + "useObjectModel": false, + "value": "JackMa" +}, +``` + +```json +{ + "useObjectModel": true, + "value": { + sameObjectId: 3 + } +} +``` + +#### relations + +```json +relation: { + // 可使用 数组(仅当to使用字符串时)、字符串 + // 不允许为null或undefined,如果两个wrapper之间无关系,宁可不写这整个对象。 + from: "", + // 可使用 数组(仅当from使用字符串时)、字符串 + // 不允许为null或undefined,如果两个wrapper之间无关系,宁可不写这整个对象。 + to: "" +} +``` + +`from`和`to`两个属性传入的字符串,必须是在`wrappers`数组中所含有的`id`属性。 + +这两个属性只有在对方为字符串时,才能设置自己为数组,以表示“一对多”关系。 + +#### beginWork + +```json +beginWork: { + // 设置全组超时时间数值,使用long(int64)值 + // 可以传入<=0的值表示不限制超时时间。 + // 允许为null、undefined,视为不限制超时时间。 + timeoutLength: 100, + // 全组超时时间单位,有以下几个取值:(即java.util.concurrent.TimeUnit的枚举值) + // "NANOSECONDS"、"MICROSECONDS"、"MILLISECONDS" + // "SECONDS"、"MINUTES"、"HOURS"、"DAYS" + // 允许为undefined和null,视为"MILLISECONDS" + // 但是除了undefined、null和以上7个值外,不允许任何值 + timeoutUnit: "MILLISECONDS", + // 传入的启动wrapper的id数组,不允许null、undefined。可以为空,但是这不符合常理。 + wrappers: [ + "first" + ], + // 执行的线程池 + // 可以传入"COMMON_POOL",代表使用asyncTool的默认线程池。 + // 可以为null或undefined,视为"COMMON_POOL" + // 也可以以如下格式传入全限定名+字段/方法名字符串,则使用反射调取该字段的所指向的ExecutorService: + // 全限定名#字段名 或 全限定名##方法名 + executor: "COMMON_POOL" +} +``` + +### 其他特殊格式 + +#### 对象模型`ObjectModel` + +* 有时我们需要指定接口实现类,或者是默认的接口实现对象,并自定义这些对象的属性。 + +* 或是指定多个json字段代表一个共同的对象。 + +那么,我们就需要高度自定义此对象的属性,并封装为`com.jd.platform.async.scheduling.model.ObjectModel`类。 + +```json +{ + // 如果需要使用asyncTool指名道姓的常量实现对象,则请见下方《常量对象规范》,并在这里传入名称字符串。 + // 当设置了该属性时,其他的属性均会被忽视。 + "constObjectName": "NOT_SKIP", + // 如果希望指定多个json字段代表一个共同的对象: + // 则将其设为相同的id。 + // 如果设置了该属性,其他的属性均会被忽视。(优先级低于constObjectName) + "sameObjectId": 1, + // 提供类的全限定名字符串,将调用无参构造方法进行初始化 + "className": "your.package.name.YourKlassName", + // 初始化后,会根据该值来修改对象属性 + "properties": { + // 其中的键值对为各字段名。这些字段需要有getter、setter方法。 + "myIntegerField": 123123 + } +} +``` + +##### 常量对象大全 + +在`constObjectName`属性中设置,用以下字符串代表如下对象: +| 对象名字符串 | 常量值 | +| -------------------------------- | ------------------------------------------------------- | +| `"NOT_SKIP"` | `SkipStrategy.NOT_SKIP` | +| `"CHECK_ONE_LEVEL"` | `SkipStrategy.CHECK_ONE_LEVEL` | +| `"ALL_DEPENDENCIES_ALL_SUCCESS"` | `DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS` | +| `"ALL_DEPENDENCIES_ANY_SUCCESS"` | `DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS` | +| `"ALL_DEPENDENCIES_NONE_FAILED"` | ` DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED` | +| `"SUCCESS_CONTINUE"` | `DependOnUpWrapperStrategy.SUCCESS_CONTINUE` | +| `"SUCCESS_START_INIT_CONTINUE"` | `DependOnUpWrapperStrategy.SUCCESS_START_INIT_CONTINUE` | +| `"PRINT_EXCEPTION_STACK_TRACE"` | `ICallback.PRINT_EXCEPTION_STACK_TRACE` | +详见请参考`com.jd.platform.async.scheduling.model.Constants`类中的具体代码。 diff --git a/README.md b/README.md index 2a7233d..24f2ab0 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,16 @@ 在V1.3后,框架支持在worker的action的入参Map中获取任意一个执行单元的执行结果,当然,可以取其中的1个、多个执行结果作为自己的入参。Key就是在定义wrapper时通过id传进来的唯一id标识。详情demo可以查看test包下dependnew包案例。 -## 并发场景可能存在的需求之——全组任务的超时 -一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。 +## 并发场景可能存在的需求之——任务的超时 +> 在v1.4中: +> +> 一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。 + +在v1.5中: + +每个wrapper可以设置自己的超时时间,同时也可以设置整组任务的超时时间。 + +且可以设置一旦超时则打断线程(默认不启用打断线程) ## 并发场景可能存在的需求之——高性能、低线程数 该框架全程无锁,不依靠线程锁来保证顺序。 @@ -114,4 +122,3 @@ ## 快速开始 [点此开启实战](https://gitee.com/jd-platform-opensource/asyncTool/blob/master/QuickStart.md) - diff --git a/asyncTool-core/pom.xml b/asyncTool-core/pom.xml new file mode 100644 index 0000000..5ed85e0 --- /dev/null +++ b/asyncTool-core/pom.xml @@ -0,0 +1,27 @@ + + + + asyncTool + com.jd.platform + 1.5.0-SNAPSHOT + + 4.0.0 + + asyncTool-core + + + 8 + 8 + + + + + com.jd.platform + asyncTool-openutil + 1.5.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/src/main/java/com/jd/platform/async/callback/DefaultCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java similarity index 66% rename from src/main/java/com/jd/platform/async/callback/DefaultCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java index 8b1d6c5..e187359 100755 --- a/src/main/java/com/jd/platform/async/callback/DefaultCallback.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java @@ -15,14 +15,10 @@ public class DefaultCallback implements ICallback { } /** - * 默认将打印存在的非{@link com.jd.platform.async.exception.SkippedException}的异常。 + * 默认情况啥回调都没有,而且将吞掉所有异常显示(只保存在{@link WorkResult}中) */ @Override public void result(boolean success, T param, WorkResult workResult) { - Exception ex = workResult.getEx(); - if (ex != null && !(ex instanceof SkippedException)) { - ex.printStackTrace(); - } + // do nothing } - } diff --git a/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java diff --git a/src/main/java/com/jd/platform/async/callback/ICallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java similarity index 48% rename from src/main/java/com/jd/platform/async/callback/ICallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java index ee71448..1087366 100755 --- a/src/main/java/com/jd/platform/async/callback/ICallback.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java @@ -1,6 +1,7 @@ package com.jd.platform.async.callback; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.worker.WorkResult; /** @@ -11,7 +12,6 @@ import com.jd.platform.async.worker.WorkResult; */ @FunctionalInterface public interface ICallback { - /** * 任务开始的监听 */ @@ -25,4 +25,22 @@ public interface ICallback { * 只要Wrapper被调用后成功或失败/超时,该方法都会被执行。 */ void result(boolean success, T param, WorkResult workResult); + + /** + * 提供常量选项:打印异常信息,跳过时的异常{@link SkippedException}不会打印。 + */ + ICallback PRINT_EXCEPTION_STACK_TRACE = new ICallback() { + @Override + public void result(boolean success, Object param, WorkResult workResult) { + Exception ex = workResult.getEx(); + if (ex != null && !(ex instanceof SkippedException)) { + ex.printStackTrace(); + } + } + + @Override + public String toString() { + return "PRINT_EXCEPTION_STACK_TRACE"; + } + }; } diff --git a/src/main/java/com/jd/platform/async/callback/IGroupCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IGroupCallback.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/IGroupCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/IGroupCallback.java diff --git a/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java diff --git a/src/main/java/com/jd/platform/async/callback/IWorker.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java similarity index 91% rename from src/main/java/com/jd/platform/async/callback/IWorker.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java index d25995c..5f74c45 100755 --- a/src/main/java/com/jd/platform/async/callback/IWorker.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java @@ -1,9 +1,9 @@ package com.jd.platform.async.callback; -import java.util.Map; - import com.jd.platform.async.wrapper.WorkerWrapper; +import java.util.Map; + /** * 每个最小执行单元需要实现该接口 * @@ -20,7 +20,7 @@ public interface IWorker { V action(T object, Map> allWrappers); /** - * 超时、异常时,返回的默认值 + * 超时、异常、跳过时,返回的默认值 * * @return 默认值 */ diff --git a/src/main/java/com/jd/platform/async/exception/SkippedException.java b/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java similarity index 100% rename from src/main/java/com/jd/platform/async/exception/SkippedException.java rename to asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java diff --git a/src/main/java/com/jd/platform/async/executor/Async.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java similarity index 95% rename from src/main/java/com/jd/platform/async/executor/Async.java rename to asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java index a38ab22..3232946 100644 --- a/src/main/java/com/jd/platform/async/executor/Async.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java @@ -7,7 +7,10 @@ import com.jd.platform.async.executor.timer.SystemClock; import com.jd.platform.async.wrapper.WorkerWrapper; import com.jd.platform.async.wrapper.WorkerWrapperGroup; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -51,13 +54,13 @@ public class Async { /** * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL */ - @SuppressWarnings("unchecked") public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { if (workerWrapper == null || workerWrapper.length == 0) { return false; } Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet()); + //noinspection unchecked return beginWork(timeout, executorService, workerWrappers); } @@ -68,6 +71,7 @@ public class Async { return beginWork(timeout, getCommonPool(), workerWrapper); } + @SuppressWarnings("unused") public static void beginWorkAsync(long timeout, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) { beginWorkAsync(timeout, getCommonPool(), groupCallback, workerWrapper); } @@ -183,11 +187,11 @@ public class Async { /** * @param now 是否立即关闭 - * @throws IllegalStateException 如果尚未调用过{@link #getCommonPool()},即没有使用过“使用默认线程池”的方法,该方法会抛出空指针异常。 + * @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。 */ - public static synchronized void shutDownCommonPool(boolean now) { + public static synchronized boolean shutDownCommonPool(boolean now) { if (COMMON_POOL == null) { - throw new IllegalStateException("COMMON_POOL Not initialized yet"); + return false; } if (!COMMON_POOL.isShutdown()) { if (now) { @@ -196,6 +200,7 @@ public class Async { COMMON_POOL.shutdown(); } } + return true; } /** diff --git a/src/main/java/com/jd/platform/async/executor/PollingCenter.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java similarity index 85% rename from src/main/java/com/jd/platform/async/executor/PollingCenter.java rename to asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java index 66d4cab..01778a2 100644 --- a/src/main/java/com/jd/platform/async/executor/PollingCenter.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java @@ -1,9 +1,9 @@ package com.jd.platform.async.executor; -import com.jd.platform.async.util.timer.Timeout; -import com.jd.platform.async.util.timer.TimerTask; -import com.jd.platform.async.util.timer.HashedWheelTimer; -import com.jd.platform.async.util.timer.Timer; +import com.jd.platform.async.openutil.timer.HashedWheelTimer; +import com.jd.platform.async.openutil.timer.Timeout; +import com.jd.platform.async.openutil.timer.Timer; +import com.jd.platform.async.openutil.timer.TimerTask; import com.jd.platform.async.wrapper.WorkerWrapperGroup; import java.util.Set; @@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit; /** * 检查{@link WorkerWrapperGroup}是否调用完成的轮询中心。 - *

+ * 内部使用时间轮进行轮询。 *

* =========================================================================================== *

@@ -27,11 +27,11 @@ import java.util.concurrent.TimeUnit; * 这是旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况,在test/v15.wrappertest中示例testThreadPolling_V14Bug说明了这个bug * 线程数:2 * A(5ms)--B1(10ms) ---|--> C1(5ms) - * . \ | (B1、B2全部完成可执行C1、C2) + * . \ | (B1、B2任一完成可执行C1、C2) * . ---> B2(20ms) --|--> C2(5ms) *

* } - * 线程1执行了A,然后在{@link java.util.concurrent.CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。 + * 线程1执行了A,然后在{@link CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。 * 线程2执行了B1或B2中的一个,也在allOf方法等待C1、C2完成。 * 结果没有线程执行C和B2了,导致超时而死,并且这个线程池线程有可能被耗尽。 * > diff --git a/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java similarity index 97% rename from src/main/java/com/jd/platform/async/executor/timer/SystemClock.java rename to asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java index e65dd85..6cba50a 100644 --- a/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java @@ -2,7 +2,6 @@ package com.jd.platform.async.executor.timer; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/src/main/java/com/jd/platform/async/worker/ResultState.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/ResultState.java similarity index 100% rename from src/main/java/com/jd/platform/async/worker/ResultState.java rename to asyncTool-core/src/main/java/com/jd/platform/async/worker/ResultState.java diff --git a/src/main/java/com/jd/platform/async/worker/WorkResult.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java similarity index 95% rename from src/main/java/com/jd/platform/async/worker/WorkResult.java rename to asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java index 6d32659..1cf0f04 100755 --- a/src/main/java/com/jd/platform/async/worker/WorkResult.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java @@ -1,7 +1,5 @@ package com.jd.platform.async.worker; -import java.util.concurrent.atomic.AtomicReference; - /** * 执行结果 */ diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java new file mode 100644 index 0000000..fd5aa17 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java @@ -0,0 +1,100 @@ +package com.jd.platform.async.wrapper; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.openutil.collection.DirectedGraph; +import com.jd.platform.async.openutil.collection.Graph; +import com.jd.platform.async.wrapper.strategy.WrapperStrategy; + +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 快速构造{@link WorkerWrapper},少废话! + *

+ * 直接设置属性,不用麻烦Builder设置来设置去, + * 请 注 意:构造方法不会检查参数合法性,请程序员自己保证参数合法。 + *

+ * 将关系存储于有向图中{@link DirectedGraph}以节省每个wrapper都要保存节点数据的开销。 + *

+ * + * @author create by TcSnZh on 2021/5/13-上午11:54 + */ +public class QuickBuildWorkerWrapper extends WorkerWrapper { + private final DirectedGraph, Object> graph; + + private volatile Set> nextWrappersCache; + private volatile Set> dependWrappersCache; + + /** + * 构造函数,传入所有属性 + * + * @param id {@link WorkerWrapper#id} + * @param param {@link WorkerWrapper#param} + * @param worker {@link WorkerWrapper#worker} + * @param callback {@link WorkerWrapper#callback} + * @param allowInterrupt {@link WorkerWrapper#allowInterrupt} + * @param enableTimeout {@link WorkerWrapper#enableTimeout} + * @param timeoutLength {@link WorkerWrapper#timeoutLength} + * @param timeoutUnit {@link WorkerWrapper#timeoutLength} + * @param wrapperStrategy {@link WorkerWrapper#timeoutUnit} + * @param wrapperGraph 将节点信息保存在图中,而不是如{@link StableWorkerWrapper}在每个wrapper中都保存节点信息。 + *

+ * {@link WorkerWrapper#getDependWrappers()}与{@link WorkerWrapper#getNextWrappers()}方法 + * 将从本图中读取依赖顺序。除此之外,本类不会对本图进行任何修改操作。 + * 因此,传入的此图应当保证读取时的线程安全。 + *

+ */ + public QuickBuildWorkerWrapper(String id, + T param, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit, + WrapperStrategy wrapperStrategy, + DirectedGraph, Object> wrapperGraph) { + super(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, wrapperStrategy); + graph = wrapperGraph; + super.param = param; + State.setState(state, State.BUILDING, State.INIT); + } + + @Override + public Set> getNextWrappers() { + if (nextWrappersCache == null) { + synchronized (this) { + if (nextWrappersCache == null) { + nextWrappersCache = graph.getRelationFrom(this).stream() + .map(Graph.Entry::getTo).collect(Collectors.toSet()); + } + } + } + return nextWrappersCache; + } + + @Override + public Set> getDependWrappers() { + if (dependWrappersCache == null) { + synchronized (this) { + if (dependWrappersCache == null) { + dependWrappersCache = graph.getRelationTo(this).stream() + .map(Graph.Entry::getFrom).collect(Collectors.toSet()); + } + } + } + return dependWrappersCache; + } + + @Override + void setNextWrappers(Set> nextWrappers) { + throw new UnsupportedOperationException(); + } + + @Override + void setDependWrappers(Set> dependWrappers) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java similarity index 100% rename from src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java diff --git a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java similarity index 90% rename from src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java index 5fed611..5c0da4a 100644 --- a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java @@ -1,11 +1,12 @@ package com.jd.platform.async.wrapper; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper; -import com.jd.platform.async.wrapper.strategy.depend.DependWrapperActionStrategy; -import com.jd.platform.async.wrapper.strategy.depend.DependWrapperStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper; import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; @@ -62,15 +63,15 @@ class StableWorkerWrapperBuilder, DependWrapperActionStrategy> dependWrapperActionStrategyMap; + private Map, DependOnUpWrapperStrategy> dependWrapperActionStrategyMap; /** * 存储需要特殊对待自己的nextWrapper集合。 */ - private Map, DependWrapperActionStrategy> selfIsSpecialMap; + private Map, DependOnUpWrapperStrategy> selfIsSpecialMap; /** * 一个保存以must=true方式传入的WorkerWrapper的集合。 *

- * 该Set将会加入到{@link WorkerWrapper.WrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中 + * 该Set将会加入到{@link WorkerWrapper.StableWrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中 */ private Set> mustDependSet; /** @@ -172,7 +173,7 @@ class StableWorkerWrapperBuilder wrapper) { + public SetDependImpl specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper) { if (strategy == null || wrapper == null) { return this; } @@ -232,7 +233,7 @@ class StableWorkerWrapperBuilder wrapper) { + public SetNextImpl specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper) { if (strategy == null || wrapper == null) { return this; } @@ -330,6 +331,7 @@ class StableWorkerWrapperBuilder 0) { @@ -341,7 +343,7 @@ class StableWorkerWrapperBuilder 0) { - DependWrapperStrategyMapper mapper = new DependWrapperStrategyMapper(); + DependOnUpWrapperStrategyMapper mapper = new DependOnUpWrapperStrategyMapper(); dependWrapperActionStrategyMap.forEach(mapper::putMapping); wrapper.getWrapperStrategy().setDependWrapperStrategyMapper(mapper); } @@ -351,12 +353,12 @@ class StableWorkerWrapperBuilder 0) { selfIsSpecialMap.forEach((next, strategy) -> { - DependWrapperStrategyMapper dependWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper(); - if (dependWrapperStrategyMapper == null) { + DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper(); + if (dependOnUpWrapperStrategyMapper == null) { next.getWrapperStrategy().setDependWrapperStrategyMapper( - dependWrapperStrategyMapper = new DependWrapperStrategyMapper()); + dependOnUpWrapperStrategyMapper = new DependOnUpWrapperStrategyMapper()); } - dependWrapperStrategyMapper.putMapping(wrapper, strategy); + dependOnUpWrapperStrategyMapper.putMapping(wrapper, strategy); }); } } @@ -381,8 +383,8 @@ class StableWorkerWrapperBuilder... wrappers) { @@ -397,8 +399,8 @@ class StableWorkerWrapperBuilder wrapper) { @@ -406,7 +408,7 @@ class StableWorkerWrapperBuilder wrapper, boolean isMust) { @@ -477,7 +479,7 @@ class StableWorkerWrapperBuilder { // ========== 固定属性 ========== @@ -41,7 +57,7 @@ public abstract class WorkerWrapper { /** * 各种策略的封装类。 */ - private final WrapperStrategy wrapperStrategy = new WrapperStrategy(); + private final WrapperStrategy wrapperStrategy; /** * 是否允许被打断 */ @@ -87,7 +103,8 @@ public abstract class WorkerWrapper { boolean allowInterrupt, boolean enableTimeout, long timeoutLength, - TimeUnit timeoutUnit + TimeUnit timeoutUnit, + WrapperStrategy wrapperStrategy ) { if (worker == null) { throw new NullPointerException("async.worker is null"); @@ -103,7 +120,17 @@ public abstract class WorkerWrapper { this.enableTimeout = enableTimeout; this.timeoutLength = timeoutLength; this.timeoutUnit = timeoutUnit; + this.wrapperStrategy = wrapperStrategy; + } + WorkerWrapper(String id, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit) { + this(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, new StableWrapperStrategy()); } // ========== public ========== @@ -141,7 +168,7 @@ public abstract class WorkerWrapper { } public State getState() { - return State.of(state.get()); + return of(state.get()); } /** @@ -193,7 +220,7 @@ public abstract class WorkerWrapper { // 就在想CAS的时候,出结果了,就采用新的结果重新判断一次 continue; } - fastFail(true, null); + fastFail(true, null, false); } return -1L; } @@ -228,37 +255,39 @@ public abstract class WorkerWrapper { // 因为抽取成方法反而不好传参、污染类方法,所以就这么干了 final Consumer __function__callbackResult = success -> { + WorkResult _workResult = getWorkResult(); try { - callback.result(success, param, getWorkResult()); + callback.result(success, param, _workResult); } catch (Exception e) { - if (State.setState(state, states_of_skipOrAfterWork, ERROR, null)) { - fastFail(false, e); + if (setState(state, states_of_skipOrAfterWork, ERROR, null)) { + fastFail(false, e, _workResult.getEx() instanceof SkippedException); } } }; - final Runnable __function__callbackResult_beginNext = + final Runnable __function__callbackResultOfFalse_beginNext = () -> { __function__callbackResult.accept(false); beginNext(executorService, now, remainTime, group); }; - final BiConsumer __function__fastFail_callbackResult_beginNext = + final BiConsumer __function__fastFail_callbackResult$false_beginNext = (fastFail_isTimeout, fastFail_exception) -> { - fastFail(fastFail_isTimeout, fastFail_exception); - __function__callbackResult_beginNext.run(); + boolean isSkip = fastFail_exception instanceof SkippedException; + fastFail(fastFail_isTimeout && !isSkip, fastFail_exception, isSkip); + __function__callbackResultOfFalse_beginNext.run(); }; final Runnable __function__doWork = () -> { - if (State.setState(state, STARTED, WORKING)) { + if (setState(state, STARTED, WORKING)) { try { fire(group); } catch (Exception e) { - if (State.setState(state, WORKING, ERROR)) { - __function__fastFail_callbackResult_beginNext.accept(false, e); + if (setState(state, WORKING, ERROR)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, e); } return; } } - if (State.setState(state, WORKING, AFTER_WORK)) { + if (setState(state, WORKING, AFTER_WORK)) { __function__callbackResult.accept(true); beginNext(executorService, now, remainTime, group); } @@ -266,20 +295,20 @@ public abstract class WorkerWrapper { // ================================================ // 开始执行 try { - if (State.isState(state, BUILDING)) { + if (isState(state, BUILDING)) { throw new IllegalStateException("wrapper can't work because state is BUILDING ! wrapper is " + this); } //总的已经超时了,就快速失败,进行下一个 if (remainTime <= 0) { - if (State.setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { - __function__fastFail_callbackResult_beginNext.accept(true, null); + if (setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { + __function__fastFail_callbackResult$false_beginNext.accept(true, null); } return; } //如果自己已经执行过了。 //可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 final AtomicReference oldStateRef = new AtomicReference<>(null); - if (!State.setState(state, states_of_notWorked, STARTED, oldStateRef::set)) { + if (!setState(state, states_of_notWorked, STARTED, oldStateRef::set)) { return; } // 如果wrapper是第一次,要调用callback.begin @@ -288,8 +317,8 @@ public abstract class WorkerWrapper { callback.begin(); } catch (Exception e) { // callback.begin 发生异常 - if (State.setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { - __function__fastFail_callbackResult_beginNext.accept(false, e); + if (setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, e); } return; } @@ -305,8 +334,8 @@ public abstract class WorkerWrapper { // 每个线程都需要判断是否要跳过自己,该方法可能会跳过正在工作的自己。 final WrapperStrategy wrapperStrategy = getWrapperStrategy(); if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) { - if (State.setState(state, STARTED, SKIP)) { - __function__fastFail_callbackResult_beginNext.accept(false, new SkippedException()); + if (setState(state, STARTED, SKIP)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, new SkippedException()); } return; } @@ -317,10 +346,10 @@ public abstract class WorkerWrapper { case TAKE_REST: return; case FAST_FAIL: - if (State.setState(state, STARTED, ERROR)) { + if (setState(state, STARTED, ERROR)) { // 根据FAST_FAIL.fastFailException()设置的属性值来设置fastFail方法的参数 ResultState resultState = judge.getResultState(); - __function__fastFail_callbackResult_beginNext.accept( + __function__fastFail_callbackResult$false_beginNext.accept( resultState == ResultState.TIMEOUT, judge.getFastFailException() ); @@ -335,10 +364,10 @@ public abstract class WorkerWrapper { } } catch (Exception e) { // wrapper本身抛出了不该有的异常 - State.setState(state, states_all, ERROR, null); + setState(state, states_all, ERROR, null); NotExpectedException ex = new NotExpectedException(e, this); workResult.set(new WorkResult<>(null, ResultState.EXCEPTION, ex)); - __function__fastFail_callbackResult_beginNext.accept(false, ex); + __function__fastFail_callbackResult$false_beginNext.accept(false, ex); } } @@ -367,10 +396,10 @@ public abstract class WorkerWrapper { * 快速失败。 * 该方法不负责检查状态,请自行控制。 * - * @param timeout 是否是因为超时而快速失败 - * @param e 设置异常信息到{@link WorkResult#getEx()} + * @param isTimeout 是否是因为超时而快速失败 + * @param e 设置异常信息到{@link WorkResult#getEx()} */ - protected void fastFail(boolean timeout, Exception e) { + protected void fastFail(boolean isTimeout, Exception e, boolean isSkip) { // 试图打断正在执行{@link IWorker#action(Object, Map)}的线程 Thread _doWorkingThread; if ((_doWorkingThread = doWorkingThread.get()) != null @@ -381,7 +410,7 @@ public abstract class WorkerWrapper { // 尚未处理过结果则设置 workResult.compareAndSet(null, new WorkResult<>( worker.defaultValue(), - timeout ? ResultState.TIMEOUT : ResultState.EXCEPTION, + isTimeout ? ResultState.TIMEOUT : (isSkip ? ResultState.DEFAULT : ResultState.EXCEPTION), e )); } @@ -406,7 +435,7 @@ public abstract class WorkerWrapper { try { next = nextWrappers.stream().findFirst().get(); group.addWrapper(next); - State.setState(state, AFTER_WORK, SUCCESS); + setState(state, AFTER_WORK, SUCCESS); } finally { PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); if (next != null) { @@ -421,7 +450,7 @@ public abstract class WorkerWrapper { nextWrappers.forEach(next -> executorService.submit(() -> next.work(executorService, this, nextRemainTIme, group)) ); - State.setState(state, AFTER_WORK, SUCCESS); + setState(state, AFTER_WORK, SUCCESS); } finally { PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); } @@ -460,6 +489,7 @@ public abstract class WorkerWrapper { /** * @deprecated 建议使用 {@link #builder()}返回{@link WorkerWrapperBuilder}接口,以调用v1.5之后的规范api */ + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public Builder() { } @@ -477,7 +507,7 @@ public abstract class WorkerWrapper { public String toString() { final StringBuilder sb = new StringBuilder(400) .append("WorkerWrapper{id=").append(id) - .append(", state=").append(State.of(state.get())) + .append(", state=").append(of(state.get())) .append(", param=").append(param) .append(", workResult=").append(workResult) .append(", allowInterrupt=").append(allowInterrupt) @@ -507,99 +537,54 @@ public abstract class WorkerWrapper { return sb.toString(); } - public static class WrapperStrategy implements DependenceStrategy, SkipStrategy { - - // ========== 这三个策略器用于链式判断是否要开始工作 ========== - - // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy - - /** - * 对特殊Wrapper专用的依赖响应策略。 - * 该值允许为null - */ - private DependWrapperStrategyMapper dependWrapperStrategyMapper; - /** - * 对必须完成的(must的)Wrapper的依赖响应策略。 - * 该值允许为null - *

- * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 - */ + /** + * 一个通用的策略器实现类,提供了修改的功能。并兼容之前的代码。 + */ + public static class StableWrapperStrategy extends WrapperStrategy.AbstractWrapperStrategy { + private DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper; private DependMustStrategyMapper dependMustStrategyMapper; - /** - * 底层全局策略。 - */ private DependenceStrategy dependenceStrategy; + private SkipStrategy skipStrategy; @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, - WorkerWrapper thisWrapper, - WorkerWrapper fromWrapper) { - // 如果存在依赖,则调用三层依赖响应策略进行判断 - DependenceStrategy strategy = dependWrapperStrategyMapper; - if (dependMustStrategyMapper != null) { - strategy = strategy == null ? dependMustStrategyMapper : strategy.thenJudge(dependMustStrategyMapper); - } - if (dependenceStrategy != null) { - strategy = strategy == null ? dependenceStrategy : strategy.thenJudge(dependenceStrategy); - } - if (strategy == null) { - throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!"); - } - return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper); - } - - public DependWrapperStrategyMapper getDependWrapperStrategyMapper() { - return dependWrapperStrategyMapper; + public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() { + return dependOnUpWrapperStrategyMapper; } - public void setDependWrapperStrategyMapper(DependWrapperStrategyMapper dependWrapperStrategyMapper) { - this.dependWrapperStrategyMapper = dependWrapperStrategyMapper; + @Override + public void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) { + this.dependOnUpWrapperStrategyMapper = dependOnUpWrapperStrategyMapper; } + @Override public DependMustStrategyMapper getDependMustStrategyMapper() { return dependMustStrategyMapper; } + @Override public void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) { this.dependMustStrategyMapper = dependMustStrategyMapper; } + @Override public DependenceStrategy getDependenceStrategy() { return dependenceStrategy; } + @Override public void setDependenceStrategy(DependenceStrategy dependenceStrategy) { this.dependenceStrategy = dependenceStrategy; } - // ========== 跳过策略 ========== - - private SkipStrategy skipStrategy; - @Override - public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - return skipStrategy != null && skipStrategy.shouldSkip(nextWrappers, thisWrapper, fromWrapper); - } - public SkipStrategy getSkipStrategy() { return skipStrategy; } + @Override public void setSkipStrategy(SkipStrategy skipStrategy) { this.skipStrategy = skipStrategy; } - - // ========== toString ========== - - @Override - public String toString() { - return "WrapperStrategy{" + - "dependWrapperStrategyMapper=" + dependWrapperStrategyMapper + - ", dependMustStrategyMapper=" + dependMustStrategyMapper + - ", dependenceStrategy=" + dependenceStrategy + - ", skipStrategy=" + skipStrategy + - '}'; - } } /** @@ -732,6 +717,7 @@ public abstract class WorkerWrapper { * * @param excepts 范围。 */ + @SuppressWarnings("unused") static boolean inStates(AtomicInteger state, State... excepts) { int current; boolean inExcepts; @@ -753,7 +739,7 @@ public abstract class WorkerWrapper { /** * CAS的判断是否是某个状态 */ - static boolean isState(AtomicInteger state, State except) { + static boolean isState(AtomicInteger state, @SuppressWarnings("SameParameterValue") State except) { return state.compareAndSet(except.id, except.id); } diff --git a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java similarity index 93% rename from src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java index 7a1f909..e5307d9 100644 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java @@ -2,7 +2,7 @@ package com.jd.platform.async.wrapper; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.wrapper.strategy.depend.DependWrapperActionStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; @@ -112,9 +112,11 @@ public interface WorkerWrapperBuilder { * @param wrapper 需要设置特殊策略的Wrapper。 * @param strategy 特殊策略。 */ - SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); + @SuppressWarnings("UnusedReturnValue") + SetDepend specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper); - default SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper... wrappers) { + @SuppressWarnings("unused") + default SetDepend specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper... wrappers) { if (strategy == null || wrappers == null) { return this; } @@ -156,6 +158,7 @@ public interface WorkerWrapperBuilder { return setDepend().wrapper(wrappers).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder depends(Collection wrappers) { return setDepend().wrapper(wrappers).end(); } @@ -164,6 +167,7 @@ public interface WorkerWrapperBuilder { return setDepend().wrapper(wrappers).strategy(strategy).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder depends(DependenceStrategy strategy, Collection wrappers) { return setDepend().wrapper(wrappers).strategy(strategy).end(); } @@ -204,6 +208,7 @@ public interface WorkerWrapperBuilder { */ SetNext mustToNextWrapper(WorkerWrapper wrapper); + @SuppressWarnings("unused") default SetNext requireToNextWrapper(WorkerWrapper wrapper, boolean must) { return must ? mustToNextWrapper(wrapper) : wrapper(wrapper); } @@ -215,7 +220,7 @@ public interface WorkerWrapperBuilder { * @param wrapper 依赖本Wrapper的下游Wrapper。 * @return 返回Builder自身。 */ - SetNext specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); + SetNext specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper); WorkerWrapperBuilder end(); } @@ -229,6 +234,7 @@ public interface WorkerWrapperBuilder { return setNext().wrapper(wrappers).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder nextOf(Collection wrappers) { return setNext().wrapper(wrappers).end(); } diff --git a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java similarity index 95% rename from src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java index 528d646..e3a0b3e 100644 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java @@ -1,9 +1,6 @@ package com.jd.platform.async.wrapper; import com.jd.platform.async.executor.PollingCenter; -import com.jd.platform.async.util.timer.Timeout; -import com.jd.platform.async.util.timer.TimerTask; -import com.jd.platform.async.worker.ResultState; import java.util.Collection; import java.util.Map; @@ -14,6 +11,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; +import com.jd.platform.async.openutil.timer.*; + /** * @author create by TcSnZh on 2021/5/9-下午7:21 */ @@ -48,6 +47,7 @@ public class WorkerWrapperGroup { Objects.requireNonNull(wrapper).forEach(this::addWrapper); } + @SuppressWarnings("unused") public void addWrapper(WorkerWrapper... wrappers) { for (WorkerWrapper wrapper : Objects.requireNonNull(wrappers)) { addWrapper(wrapper); @@ -76,6 +76,7 @@ public class WorkerWrapperGroup { public class CheckFinishTask implements TimerTask { + @SuppressWarnings("RedundantThrows") @Override public void run(Timeout timeout) throws Exception { // 已经完成了 @@ -142,7 +143,7 @@ public class WorkerWrapperGroup { if (obj == this) { return true; } - if (!(obj instanceof WorkerWrapperGroup.CheckFinishTask)) { + if (!(obj instanceof CheckFinishTask)) { return false; } return Objects.equals(WorkerWrapperGroup.this, ((CheckFinishTask) obj).getParent()); diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java new file mode 100644 index 0000000..c1229fc --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java @@ -0,0 +1,133 @@ +package com.jd.platform.async.wrapper.strategy; + +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; + +import java.util.Set; + +/** + * @author create by TcSnZh on 2021/5/17-下午6:23 + */ +public interface WrapperStrategy extends DependenceStrategy, SkipStrategy { + // ========== 这三个策略器用于链式判断是否要开始工作 ========== + + // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy + + /** + * 设置对特殊Wrapper专用的依赖响应策略。 + * + * @return 该值允许为null + */ + DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper(); + + /** + * 对必须完成的(must的)Wrapper的依赖响应策略。 + * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 + * + * @return 该值允许为null + * @deprecated 不推荐使用,很有可能被遗弃 + */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + DependMustStrategyMapper getDependMustStrategyMapper(); + + /** + * 底层全局策略。 + * + * @return 该值不允许为null + */ + DependenceStrategy getDependenceStrategy(); + + // ========== 这是跳过策略 ========== + + /** + * 跳过策略 + * + * @return 不允许为null + */ + SkipStrategy getSkipStrategy(); + + @Override + default DependenceAction.WithProperty judgeAction(Set> dependWrappers, + WorkerWrapper thisWrapper, + WorkerWrapper fromWrapper) { + // 如果存在依赖,则调用三层依赖响应策略进行判断 + + DependenceStrategy strategy = getDependWrapperStrategyMapper(); + if (getDependMustStrategyMapper() != null) { + strategy = strategy == null ? getDependMustStrategyMapper() : strategy.thenJudge(getDependenceStrategy()); + } + if (getDependenceStrategy() != null) { + strategy = strategy == null ? getDependenceStrategy() : strategy.thenJudge(getDependenceStrategy()); + } + if (strategy == null) { + throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!"); + } + return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper); + } + + @Override + default boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return getSkipStrategy() != null && getSkipStrategy().shouldSkip(nextWrappers, thisWrapper, fromWrapper); + } + + default void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) { + throw new UnsupportedOperationException(); + } + + default void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) { + throw new UnsupportedOperationException(); + } + + default void setDependenceStrategy(DependenceStrategy dependenceStrategy) { + throw new UnsupportedOperationException(); + } + + default void setSkipStrategy(SkipStrategy skipStrategy) { + throw new UnsupportedOperationException(); + } + + /** + * 抽象策略器,实现了toString + */ + abstract class AbstractWrapperStrategy implements WrapperStrategy { + @Override + public String toString() { + return "WrapperStrategy{" + + "dependWrapperStrategyMapper=" + getDependWrapperStrategyMapper() + + ", dependMustStrategyMapper=" + getDependMustStrategyMapper() + + ", dependenceStrategy=" + getDependenceStrategy() + + ", skipStrategy=" + getSkipStrategy() + + '}'; + } + } + + /** + * 默认策略器,用默认值实现了所有属性。 + */ + class DefaultWrapperStrategy extends AbstractWrapperStrategy { + @Override + public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() { + return null; + } + + @Override + public DependMustStrategyMapper getDependMustStrategyMapper() { + return null; + } + + @Override + public DependenceStrategy getDependenceStrategy() { + return DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS; + } + + @Override + public SkipStrategy getSkipStrategy() { + return SkipStrategy.CHECK_ONE_LEVEL; + } + } +} diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java similarity index 97% rename from src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java index ff1108a..b4b1a24 100644 --- a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java @@ -26,7 +26,7 @@ public class DependMustStrategyMapper implements DependenceStrategy { * 如果所有的Wrapper已经完成,本Wrapper将会开始工作。 *

* 如果任一{@link #mustDependSet}中的Wrapper失败,则返回{@link DependenceAction#FAST_FAIL}。 - * 具体超时/异常则根据{@link com.jd.platform.async.worker.ResultState}的值进行判断。 + * 具体超时/异常则根据{@link ResultState}的值进行判断。 *

* 如果存在Wrapper未完成 且 所有的Wrapper都未失败,则返回{@link DependenceAction#JUDGE_BY_AFTER}。 *

diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java similarity index 87% rename from src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java index fa5cef2..a5f0d03 100644 --- a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java @@ -3,14 +3,14 @@ package com.jd.platform.async.wrapper.strategy.depend; import com.jd.platform.async.wrapper.WorkerWrapper; /** - * 单参数策略。 + * 由上游wrapper决定本wrapper行为的单参数策略。 * * @author create by TcSnZh on 2021/5/1-下午11:16 */ @FunctionalInterface -public interface DependWrapperActionStrategy { +public interface DependOnUpWrapperStrategy { /** - * 仅使用一个参数的判断方法 + * 仅使用一个参数(即调用自身的上游wrapper)的判断方法 * * @param fromWrapper 调用本Wrapper的上游Wrapper * @return 返回 {@link DependenceAction.WithProperty} @@ -24,7 +24,7 @@ public interface DependWrapperActionStrategy { * 未运行时,休息。 * 失败时,失败。 */ - DependWrapperActionStrategy SUCCESS_CONTINUE = new DependWrapperActionStrategy() { + DependOnUpWrapperStrategy SUCCESS_CONTINUE = new DependOnUpWrapperStrategy() { @Override public DependenceAction.WithProperty judge(WorkerWrapper ww) { switch (ww.getWorkResult().getResultState()) { @@ -50,7 +50,7 @@ public interface DependWrapperActionStrategy { * 未运行时,交给下一个策略器判断。 * 失败时,失败。 */ - DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE = new DependWrapperActionStrategy() { + DependOnUpWrapperStrategy SUCCESS_START_INIT_CONTINUE = new DependOnUpWrapperStrategy() { @Override public DependenceAction.WithProperty judge(WorkerWrapper ww) { switch (ww.getWorkResult().getResultState()) { diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java similarity index 77% rename from src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java index 802e1c2..476aa05 100644 --- a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java @@ -10,12 +10,12 @@ import java.util.stream.Collectors; /** * 对不同的{@link WorkerWrapper}调用者实行个性化依赖响应策略。 *

- * 使用{@link DependWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强, + * 使用{@link DependOnUpWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强, * * @author create by TcSnZh on 2021/5/1-下午11:12 */ -public class DependWrapperStrategyMapper implements DependenceStrategy { - private final Map, DependWrapperActionStrategy> mapper = new ConcurrentHashMap<>(4); +public class DependOnUpWrapperStrategyMapper implements DependenceStrategy { + private final Map, DependOnUpWrapperStrategy> mapper = new ConcurrentHashMap<>(4); /** * 设置对应策略 @@ -24,7 +24,8 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { * @param strategy 要设置的策略 * @return 返回this,链式调用。 */ - public DependWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependWrapperActionStrategy strategy) { + @SuppressWarnings("UnusedReturnValue") + public DependOnUpWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependOnUpWrapperStrategy strategy) { mapper.put(targetWrapper, strategy); toStringCache = null; return this; @@ -33,7 +34,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { /** * 判断方法。 *

- * 如果fromWrapper在{@link #mapper}中,则返回{@link DependWrapperActionStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER} + * 如果fromWrapper在{@link #mapper}中,则返回{@link DependOnUpWrapperStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER} * * @param dependWrappers (这里不会使用该值)thisWrapper.dependWrappers的属性值。 * @param thisWrapper (这里不会使用该值)thisWrapper,即为“被催促”的WorkerWrapper @@ -44,7 +45,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - DependWrapperActionStrategy strategy = mapper.get(fromWrapper); + DependOnUpWrapperStrategy strategy = mapper.get(fromWrapper); if (strategy == null) { return DependenceAction.JUDGE_BY_AFTER.emptyProperty(); } diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java similarity index 100% rename from src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java similarity index 95% rename from src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java index be00042..a075369 100644 --- a/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java @@ -1,10 +1,13 @@ package com.jd.platform.async.wrapper.strategy.depend; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; -import java.util.*; +import java.util.Collections; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; @@ -35,7 +38,7 @@ public interface DependenceStrategy { * @param fromWrapper 调用来源Wrapper。 *

* 该参数不会为null。 - * 因为在{@link WorkerWrapper#work(ExecutorService, long, Map, WrapperEndingInspector)}方法中传入的的第一批无依赖的Wrapper, + * 因为在{@link WorkerWrapper#work(ExecutorService, long, WorkerWrapperGroup)}方法中传入的的第一批无依赖的Wrapper, * 不会被该策略器所判断,而是不论如何直接执行。 *

* @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty} @@ -135,6 +138,7 @@ public interface DependenceStrategy { case EXCEPTION: resultState = !hasFailed ? workResult.getResultState() : resultState; fastFailException = !hasFailed ? workResult.getEx() : fastFailException; + // 跳过不算失败 hasFailed = true; break; default: @@ -154,8 +158,10 @@ public interface DependenceStrategy { }; /** - * 如果被依赖的工作中任一失败,则立即失败。否则就开始工作(不论之前的工作有没有开始)。 + * 如果被依赖的工作中任一失败,则立即失败。 + * 否则就开始工作(不论之前的工作有没有开始)。 */ + @SuppressWarnings("unused") DependenceStrategy ALL_DEPENDENCIES_NONE_FAILED = new DependenceStrategy() { @Override public DependenceAction.WithProperty judgeAction(Set> dependWrappers, @@ -166,7 +172,7 @@ public interface DependenceStrategy { switch (workResult.getResultState()) { case TIMEOUT: case EXCEPTION: - return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx()); + return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx()); default: } } @@ -187,6 +193,7 @@ public interface DependenceStrategy { * @param theseWrapper 该方法唯一有效参数。 * @return 返回生成的 {@link DependenceAction.WithProperty) */ + @SuppressWarnings("unused") static DependenceStrategy theseWrapperAllSuccess(Set> theseWrapper) { return new DependenceStrategy() { private final Set> theseWrappers; @@ -239,6 +246,7 @@ public interface DependenceStrategy { * * @deprecated 不推荐使用must开关 */ + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() { @Override diff --git a/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java similarity index 98% rename from src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java index 13bd5a1..1b4f981 100644 --- a/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java @@ -2,7 +2,7 @@ package com.jd.platform.async.wrapper.strategy.skip; import com.jd.platform.async.wrapper.WorkerWrapper; -import java.util.*; +import java.util.Set; /** * @author create by TcSnZh on 2021/5/6-下午3:02 diff --git a/src/test/java/beforev14/depend/DeWorker.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker.java similarity index 100% rename from src/test/java/beforev14/depend/DeWorker.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker.java diff --git a/src/test/java/beforev14/depend/DeWorker1.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker1.java similarity index 100% rename from src/test/java/beforev14/depend/DeWorker1.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker1.java diff --git a/src/test/java/beforev14/depend/DeWorker2.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker2.java similarity index 100% rename from src/test/java/beforev14/depend/DeWorker2.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker2.java diff --git a/src/test/java/beforev14/depend/Test.java b/asyncTool-core/src/test/java/beforev14/depend/Test.java similarity index 100% rename from src/test/java/beforev14/depend/Test.java rename to asyncTool-core/src/test/java/beforev14/depend/Test.java diff --git a/src/test/java/beforev14/depend/User.java b/asyncTool-core/src/test/java/beforev14/depend/User.java similarity index 100% rename from src/test/java/beforev14/depend/User.java rename to asyncTool-core/src/test/java/beforev14/depend/User.java diff --git a/src/test/java/beforev14/dependnew/DeWorker.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker.java similarity index 100% rename from src/test/java/beforev14/dependnew/DeWorker.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker.java diff --git a/src/test/java/beforev14/dependnew/DeWorker1.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker1.java similarity index 100% rename from src/test/java/beforev14/dependnew/DeWorker1.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker1.java diff --git a/src/test/java/beforev14/dependnew/DeWorker2.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker2.java similarity index 100% rename from src/test/java/beforev14/dependnew/DeWorker2.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker2.java diff --git a/src/test/java/beforev14/dependnew/Test.java b/asyncTool-core/src/test/java/beforev14/dependnew/Test.java similarity index 100% rename from src/test/java/beforev14/dependnew/Test.java rename to asyncTool-core/src/test/java/beforev14/dependnew/Test.java diff --git a/src/test/java/beforev14/dependnew/User.java b/asyncTool-core/src/test/java/beforev14/dependnew/User.java similarity index 100% rename from src/test/java/beforev14/dependnew/User.java rename to asyncTool-core/src/test/java/beforev14/dependnew/User.java diff --git a/src/test/java/beforev14/parallel/ParTimeoutWorker.java b/asyncTool-core/src/test/java/beforev14/parallel/ParTimeoutWorker.java similarity index 100% rename from src/test/java/beforev14/parallel/ParTimeoutWorker.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParTimeoutWorker.java diff --git a/src/test/java/beforev14/parallel/ParWorker.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker.java similarity index 100% rename from src/test/java/beforev14/parallel/ParWorker.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker.java diff --git a/src/test/java/beforev14/parallel/ParWorker1.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker1.java similarity index 100% rename from src/test/java/beforev14/parallel/ParWorker1.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker1.java diff --git a/src/test/java/beforev14/parallel/ParWorker2.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker2.java similarity index 100% rename from src/test/java/beforev14/parallel/ParWorker2.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker2.java diff --git a/src/test/java/beforev14/parallel/ParWorker3.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker3.java similarity index 100% rename from src/test/java/beforev14/parallel/ParWorker3.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker3.java diff --git a/src/test/java/beforev14/parallel/ParWorker4.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker4.java similarity index 100% rename from src/test/java/beforev14/parallel/ParWorker4.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker4.java diff --git a/src/test/java/beforev14/parallel/TestPar.java b/asyncTool-core/src/test/java/beforev14/parallel/TestPar.java similarity index 100% rename from src/test/java/beforev14/parallel/TestPar.java rename to asyncTool-core/src/test/java/beforev14/parallel/TestPar.java diff --git a/src/test/java/beforev14/seq/SeqTimeoutWorker.java b/asyncTool-core/src/test/java/beforev14/seq/SeqTimeoutWorker.java similarity index 100% rename from src/test/java/beforev14/seq/SeqTimeoutWorker.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqTimeoutWorker.java diff --git a/src/test/java/beforev14/seq/SeqWorker.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker.java similarity index 100% rename from src/test/java/beforev14/seq/SeqWorker.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker.java diff --git a/src/test/java/beforev14/seq/SeqWorker1.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker1.java similarity index 100% rename from src/test/java/beforev14/seq/SeqWorker1.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker1.java diff --git a/src/test/java/beforev14/seq/SeqWorker2.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker2.java similarity index 100% rename from src/test/java/beforev14/seq/SeqWorker2.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker2.java diff --git a/src/test/java/beforev14/seq/TestSequential.java b/asyncTool-core/src/test/java/beforev14/seq/TestSequential.java similarity index 100% rename from src/test/java/beforev14/seq/TestSequential.java rename to asyncTool-core/src/test/java/beforev14/seq/TestSequential.java diff --git a/src/test/java/beforev14/seq/TestSequentialTimeout.java b/asyncTool-core/src/test/java/beforev14/seq/TestSequentialTimeout.java similarity index 100% rename from src/test/java/beforev14/seq/TestSequentialTimeout.java rename to asyncTool-core/src/test/java/beforev14/seq/TestSequentialTimeout.java diff --git a/src/test/java/v15/cases/Case1.java b/asyncTool-core/src/test/java/v15/cases/Case1.java similarity index 100% rename from src/test/java/v15/cases/Case1.java rename to asyncTool-core/src/test/java/v15/cases/Case1.java diff --git a/src/test/java/v15/cases/Case2.java b/asyncTool-core/src/test/java/v15/cases/Case2.java similarity index 100% rename from src/test/java/v15/cases/Case2.java rename to asyncTool-core/src/test/java/v15/cases/Case2.java diff --git a/src/test/java/v15/cases/Case3.java b/asyncTool-core/src/test/java/v15/cases/Case3.java similarity index 96% rename from src/test/java/v15/cases/Case3.java rename to asyncTool-core/src/test/java/v15/cases/Case3.java index d149e07..01e82a5 100644 --- a/src/test/java/v15/cases/Case3.java +++ b/asyncTool-core/src/test/java/v15/cases/Case3.java @@ -56,6 +56,8 @@ class Case3 { wrapper(id=B2) is working wrapper(id=C2) is working wrapper(id=C1) is working + wrapper(id=B4) is working + // 我们看到B5被跳过了,没有执行callback */ } } diff --git a/src/test/java/v15/cases/Case4.java b/asyncTool-core/src/test/java/v15/cases/Case4.java similarity index 100% rename from src/test/java/v15/cases/Case4.java rename to asyncTool-core/src/test/java/v15/cases/Case4.java diff --git a/src/test/java/v15/cases/Case5.java b/asyncTool-core/src/test/java/v15/cases/Case5.java similarity index 100% rename from src/test/java/v15/cases/Case5.java rename to asyncTool-core/src/test/java/v15/cases/Case5.java diff --git a/src/test/java/v15/cases/Case6.java b/asyncTool-core/src/test/java/v15/cases/Case6.java similarity index 89% rename from src/test/java/v15/cases/Case6.java rename to asyncTool-core/src/test/java/v15/cases/Case6.java index d54d855..552fecd 100644 --- a/src/test/java/v15/cases/Case6.java +++ b/asyncTool-core/src/test/java/v15/cases/Case6.java @@ -4,14 +4,9 @@ import com.jd.platform.async.executor.Async; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.wrapper.WorkerWrapper; import com.jd.platform.async.wrapper.WorkerWrapperBuilder; -import com.jd.platform.async.wrapper.strategy.depend.DependWrapperActionStrategy; import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; -import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; -import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * 示例:自定义依赖策略--对单个wrapper设置“上克下”策略--简单使用与示例 diff --git a/src/test/java/v15/cases/Case7.java b/asyncTool-core/src/test/java/v15/cases/Case7.java similarity index 100% rename from src/test/java/v15/cases/Case7.java rename to asyncTool-core/src/test/java/v15/cases/Case7.java diff --git a/src/test/java/v15/cases/Case8.java b/asyncTool-core/src/test/java/v15/cases/Case8.java similarity index 99% rename from src/test/java/v15/cases/Case8.java rename to asyncTool-core/src/test/java/v15/cases/Case8.java index 2972c9d..c91b83a 100644 --- a/src/test/java/v15/cases/Case8.java +++ b/asyncTool-core/src/test/java/v15/cases/Case8.java @@ -67,6 +67,7 @@ class Case8 { wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) + ... 以下异常信息省略 */ } diff --git a/asyncTool-core/src/test/java/v15/cases/Case9.java b/asyncTool-core/src/test/java/v15/cases/Case9.java new file mode 100644 index 0000000..a55fe08 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case9.java @@ -0,0 +1,59 @@ +package v15.cases; + +import com.jd.platform.async.callback.DefaultCallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.openutil.collection.CommonDirectedGraph; +import com.jd.platform.async.openutil.collection.DirectedGraph; +import com.jd.platform.async.wrapper.QuickBuildWorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.strategy.WrapperStrategy; + +import java.util.concurrent.*; + +/** + * 快速构造示例。 + * + * @author create by TcSnZh on 2021/5/17-下午5:23 + */ +class Case9 { + public static void main(String[] args) throws ExecutionException, InterruptedException { + DirectedGraph, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>()); + QuickBuildWorkerWrapper w1 = new QuickBuildWorkerWrapper<>("id1", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 1"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + QuickBuildWorkerWrapper w2 = new QuickBuildWorkerWrapper<>("id2", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 2"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + graph.addNode(w1, w2); + graph.putRelation(w1, new Object(), w2); + +// System.out.println(graph); + + Async.beginWork(200, w1); + + System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); + + } +} diff --git a/src/test/java/v15/wrappertest/Test.java b/asyncTool-core/src/test/java/v15/wrappertest/Test.java similarity index 100% rename from src/test/java/v15/wrappertest/Test.java rename to asyncTool-core/src/test/java/v15/wrappertest/Test.java diff --git a/asyncTool-openutil/pom.xml b/asyncTool-openutil/pom.xml new file mode 100644 index 0000000..e5ef877 --- /dev/null +++ b/asyncTool-openutil/pom.xml @@ -0,0 +1,19 @@ + + + + asyncTool + com.jd.platform + 1.5.0-SNAPSHOT + + 4.0.0 + + asyncTool-openutil + + + 8 + 8 + + + \ No newline at end of file diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java new file mode 100644 index 0000000..5f7c749 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java @@ -0,0 +1,109 @@ +package com.jd.platform.async.openutil; + +import java.util.Comparator; + +/** + * 两个int值。重写了{@link #hashCode()}与{@link #equals(Object)} + * + * @author create by TcSnZh on 2021/5/16-上午1:50 + */ +public final class BiInt { + // properties + + private final int m; + private final int n; + + public static final Comparator cmp_m_asc = Comparator.comparingInt(BiInt::getM); + public static final Comparator cmp_n_asc = Comparator.comparingInt(BiInt::getN); + public static final Comparator cmp_m_desc = cmp_m_asc.reversed(); + public static final Comparator cmp_n_desc = cmp_n_asc.reversed(); + public static final Comparator cmp_m_asc_n_asc = + cmp_m_asc.thenComparing(cmp_n_asc); + public static final Comparator cmp_m_asc_n_desc = + cmp_m_asc.thenComparing(cmp_n_desc); + public static final Comparator cmp_m_desc_n_asc = + cmp_m_desc.thenComparing(cmp_n_asc); + public static final Comparator cmp_m_desc_n_desc = + cmp_m_desc.thenComparing(cmp_n_desc); + public static final Comparator cmp_n_asc_m_asc = + cmp_n_asc.thenComparing(cmp_m_asc); + public static final Comparator cmp_n_asc_m_desc = + cmp_n_asc.thenComparing(cmp_m_desc); + public static final Comparator cmp_n_desc_m_asc = + cmp_n_desc.thenComparing(cmp_m_asc); + public static final Comparator cmp_n_desc_m_desc = + cmp_n_desc.thenComparing(cmp_m_desc); + + /** + * private constructor , please use {@link #of(int, int)} to build Idx object. + */ + private BiInt(int m, int n) { + this.m = m; + this.n = n; + } + + // getter + + public int getM() { + return m; + } + + public int getN() { + return n; + } + + // hashcode and equals + + @Override + public int hashCode() { + return m ^ n; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BiInt)) + return false; + BiInt idx = (BiInt) o; + return m == idx.m && n == idx.n; + } + + // toString + + @Override + public String toString() { + return "(" + m + ',' + n + ')'; + } + + // ========== static ========== + + + // 工厂方法 + + public static BiInt of(int m, int n) { + if (m == Integer.MIN_VALUE && n == Integer.MAX_VALUE) { + return MIN_TO_MAX; + } + if (m >= 0 && m < CACHE_RANGE_M && n >= 0 && n < CACHE_RANGE_M) { + return cache[m * CACHE_RANGE_M + n]; + } + return new BiInt(m, n); + } + + // 缓存区间 + + private static final BiInt MIN_TO_MAX = new BiInt(Integer.MIN_VALUE, Integer.MAX_VALUE); + private static final BiInt[] cache; // m from 0 to 31 , n from 0 to 31 , total 1023 . + private static final int CACHE_RANGE_M = 32; // 0 to 31 + private static final int CACHE_RANGE_N = 32; // 0 to 31 + + static { + cache = new BiInt[CACHE_RANGE_M * CACHE_RANGE_N]; + for (int i = 0; i < CACHE_RANGE_M; i++) { + for (int j = 0; j < CACHE_RANGE_N; j++) { + cache[i * CACHE_RANGE_M + j] = new BiInt(i, j); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java new file mode 100644 index 0000000..66c8939 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java @@ -0,0 +1,74 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.Iterator; + +/** + * @author create by TcSnZh on 2021/5/14-下午9:51 + */ +public abstract class AbstractArray2D implements Array2D { + /** + * 用于代替null + */ + protected static final Object NULL = new Object() { + @Override + public String toString() { + return "null"; + } + + @Override + public int hashCode() { + return 0; + } + + @SuppressWarnings("EqualsDoesntCheckParameterClass") + @Override + public boolean equals(Object obj) { + //noinspection ConstantConditions + return obj == null || obj == NULL || obj.equals(null); + } + }; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64).append(this.getClass().getSimpleName()).append('{'); + Iterator> it = iterator(); + if (it.hasNext()) { + while (true) { + Point point = it.next(); + sb.append('{').append(point.getIdx()).append(':').append(point.getElement()).append('}'); + if (!it.hasNext()) { + break; + } + sb.append(", "); + } + } + return sb.append('}').toString(); + } + + public static class PointImpl implements Point { + private final BiInt idx; + private final E element; + + public PointImpl(BiInt idx, E element) { + this.idx = idx; + this.element = element; + } + + @Override + public BiInt getIdx() { + return idx; + } + + @Override + public E getElement() { + return element; + } + + @Override + public String toString() { + return "{" + idx + ":" + element + "}"; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java new file mode 100644 index 0000000..cb971d5 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java @@ -0,0 +1,88 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; + +/** + * 抽象有向图 + * + * @author create by TcSnZh on 2021/5/13-上午11:37 + */ +public abstract class AbstractDirectedGraph implements DirectedGraph { + + @Override + public String toString() { + Set nv = nodesView(); + Set> rSet = getRelations(); + StringBuilder sb = new StringBuilder(nv.size() * 10 + rSet.size() * 20) + .append(this.getClass().getSimpleName()).append("{nodes=["); + Iterator nit = nodesView().iterator(); + if (nit.hasNext()) { + for (; ; ) { + sb.append(nit.next()); + if (!nit.hasNext()) { + break; + } + sb.append(", "); + } + } + sb.append("], relations=["); + Iterator> eit = rSet.iterator(); + if (eit.hasNext()) { + for (; ; ) { + sb.append(eit.next()); + if (!eit.hasNext()) { + break; + } + sb.append(", "); + } + } + return sb.append("]}").toString(); + } + + public abstract class AbstractNodesView extends AbstractSet { + @Override + public boolean add(N n) { + return AbstractDirectedGraph.this.addNode(n); + } + + @Override + public boolean remove(Object o) { + N o1; + //noinspection unchecked + if (!AbstractDirectedGraph.this.containsNode(o1 = (N) o)) { + return false; + } + AbstractDirectedGraph.this.removeNode(o1); + return true; + } + } + + public static abstract class AbstractEntry implements Entry { + @Override + public int hashCode() { + return this.getFrom().hashCode() ^ this.getTo().hashCode() ^ this.getRelation().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Graph.Entry)) { + return false; + } + Entry obj1 = (Entry) obj; + return Objects.equals(this.getFrom(), obj1.getFrom()) + && Objects.equals(this.getTo(), obj1.getTo()) + && Objects.equals(this.getRelation(), obj1.getRelation()); + } + + @Override + public String toString() { + return "{from=" + getFrom() + ", relation=" + getRelation() + ", to=" + getTo() + "]"; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java new file mode 100644 index 0000000..6caea3c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java @@ -0,0 +1,22 @@ +package com.jd.platform.async.openutil.collection; + +/** + * @author create by TcSnZh on 2021/5/14-上午2:33 + */ +public abstract class AbstractStoreArk implements StoreArk { + + @Override + public boolean isEmpty() { + return size() <= 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(size() * 10).append(this.getClass().getSimpleName()).append("{"); + if (!isEmpty()) { + stream().forEach(entry -> sb.append(entry.getKey()).append(":").append(entry.getValue()).append(", ")); + sb.delete(sb.length() - 2, sb.length()); + } + return sb.append("}").toString(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java new file mode 100644 index 0000000..c4bcc64 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java @@ -0,0 +1,169 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.*; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * 二维数组 + * + * @author create by TcSnZh on 2021/5/14-下午9:50 + */ +@SuppressWarnings("unused") +public interface Array2D extends Iterable> { + /** + * 有多少行 + */ + int lineLength(); + + /** + * 有多少列 + */ + int columnLength(); + + /** + * 添加元素到指定位置 + * + * @param line 行 + * @param column 列 + * @param element 元素 + * @return 如果之前添加过元素,将返回替换掉的之前的元素 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + E add(int line, int column, E element); + + /** + * 如果不存在的话则添加元素 + *

+ * {@link #add(int, int, Object)} + * + * @return 不存在且成功添加,返回true。 + */ + default boolean addIfAbsent(int line, int column, E element) { + if (get(line, column) != null) { + return false; + } + add(line, column, element); + return true; + } + + /** + * 删除元素 + * + * @param line 行 + * @param column 列 + * @return 返回移出的元素 + * @throws IndexOutOfBoundsException 行列超出返回 + * @throws IllegalArgumentException 如果原本不存在元素 + */ + E remove(int line, int column); + + /** + * 存在则移除,不存在则返回null + * + * @param line 行 + * @param column 列 + * @return 如果不存在,返回null。存在则返回被移出的元素。 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + default E removeIfAbsent(int line, int column) { + if (get(line, column) == null) { + return null; + } + return remove(line, column); + } + + /** + * 获取元素 + * + * @param line 行 + * @param column 列 + * @return 如果存在,返回该元素。不存在则返回null。 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + E get(int line, int column); + + /** + * 是否包含元素 + * + * @param element 元素 + * @return 有这个元素就返回true。 + */ + boolean containsElement(E element); + + /** + * 获取整行的元素 + * + * @param line 行号 + * @return 返回key为列号,value为元素的Map + * @throws IndexOutOfBoundsException 行号超出范围 + */ + Map fullLine(int line); + + /** + * 获取整列的元素 + * + * @param column 列号 + * @return 返回key为行号,value为元素的Map + * @throws IndexOutOfBoundsException 列号超出范围 + */ + Map fullColumn(int column); + + /** + * 迭代器 + * + * @param foreachOrder 遍历顺序 + * @return 如果本容器不允许null值存在,只需返回存在的元素的键即可。如果允许null值存在,仅需返回包括人工放入的null值的键即可。 + */ + Iterator> iterator(Comparator foreachOrder); + + @Override + default Iterator> iterator() { + //noinspection unchecked + return (Iterator) iterator(BiInt.cmp_m_asc_n_asc); + } + + /** + * 流 + */ + default Stream> stream() { + return StreamSupport.stream(spliterator(), false); + } + + default Stream> parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + + default Spliterator> spliterator(Comparator foreachOrder) { + return Spliterators.spliteratorUnknownSize(iterator(foreachOrder), 0); + } + + default Stream> stream(Comparator foreachOrder) { + return StreamSupport.stream(spliterator(foreachOrder), false); + } + + default Stream> parallelStream(Comparator foreachOrder) { + return StreamSupport.stream(spliterator(foreachOrder), true); + } + + /** + * 端点 + * + * @param 元素泛型 + */ + interface Point { + BiInt getIdx(); + + default int getLine() { + return getIdx().getM(); + } + + default int getColumn() { + return getIdx().getN(); + } + + E getElement(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java new file mode 100644 index 0000000..0ed4d44 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java @@ -0,0 +1,76 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Function; + +/** + * 二叉树 + * + * @author create by TcSnZh on 2021/5/15-下午8:00 + */ +public interface BiTree extends Tree { + // todo + + /** + * 二叉树节点 + */ + interface BiNode extends Node { + @Override + default BiNode getParent() { + throw new UnsupportedOperationException("Get parent node is not supported."); + } + + BiNode getLeft(); + + BiNode getRight(); + + @Override + default Collection getChildren() { + return Arrays.asList(getLeft(), getRight()); + } + } + + /** + * 返回一个通俗易懂的字符画。 + *

+ * 从leetcode上抄的。若有侵权请联系 {@code zh0u.he@qq.com},将会删除。 + * + * @param node 根节点 + * @param provideName 节点显示在图中的名字。 + * @param 根节点泛型 + * @return 返回一个字符画 + */ + static String toPrettyString(N node, Function provideName) { + StringBuilder sb = new StringBuilder(); + //noinspection unchecked + _toPrettyString(node, "", true, sb, (Function) provideName); + return sb.toString(); + } + + /** + * jdk8没有private static,只能加条下划线意思意思了。 + */ + static void _toPrettyString(N node, + String prefix, + boolean isLeft, + StringBuilder sb, + Function provideName) { + if (node == null) { + sb.append("(Empty tree)"); + return; + } + + BiNode right = node.getRight(); + if (right != null) { + _toPrettyString(right, prefix + (isLeft ? "│ " : " "), false, sb, provideName); + } + + sb.append(prefix).append(isLeft ? "└── " : "┌── ").append(provideName.apply(node)).append('\n'); + + BiNode left = node.getLeft(); + if (left != null) { + _toPrettyString(left, prefix + (isLeft ? " " : "│ "), true, sb, provideName); + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java new file mode 100644 index 0000000..a8a1362 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java @@ -0,0 +1,60 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 一个缓存元素位置的储存柜。 + * + * @author create by TcSnZh on 2021/5/14-上午2:37 + */ +public class CachedStoreArk extends AbstractStoreArk { + private final StoreArk inner; + + private final Map cacheMap = new HashMap<>(); + + public CachedStoreArk() { + this(CommonStoreArk::new); + } + + private CachedStoreArk(Supplier> sup) { + this.inner = sup.get(); + } + + @Override + public int store(E element) { + int id = inner.store(element); + cacheMap.put(element, id); + return id; + } + + @Override + public E peek(int id) { + return inner.peek(id); + } + + @Override + public E takeOut(int id) { + E e = inner.takeOut(id); + cacheMap.remove(e); + return e; + } + + @Override + public int size() { + return inner.size(); + } + + @Override + public Iterator> iterator() { + return inner.iterator(); + } + + @Override + public int findId(E element) { + Integer idNullable = cacheMap.get(element); + return idNullable == null ? -1 : idNullable; + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java new file mode 100644 index 0000000..f5e72dd --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java @@ -0,0 +1,10 @@ +package com.jd.platform.async.openutil.collection; + +/** + * 冲突范围表 + * + * @author create by TcSnZh on 2021/5/16-上午1:36 + */ +public class CollisionRangeTable { + // todo +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java new file mode 100644 index 0000000..0683d2c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java @@ -0,0 +1,145 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 线程不安全的有向图。 + *

+ * 不允许放入null。 + * + * @author create by TcSnZh on 2021/5/14-上午2:22 + */ +public class CommonDirectedGraph extends AbstractDirectedGraph { + + // ========== properties ========== + + private final StoreArk nodes = new CachedStoreArk<>(); + private final Array2D arr = new SparseArray2D<>(); + + // ========== methods ========== + + @Override + public boolean addNode(N node) { + if (containsNode(Objects.requireNonNull(node))) { + return false; + } + nodes.store(node); + return true; + } + + @Override + public boolean containsNode(N node) { + return node != null && findNodeId(node, false) >= 0; + } + + @Override + public Set> removeNode(N node) { + int id = findNodeId(Objects.requireNonNull(node), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为from的键 + arr.fullLine(id).forEach((toNodeId, relation) -> { + res.add(new OuterEntry<>(node, nodes.peek(toNodeId), relation)); + arr.remove(id, toNodeId); + }); + // 查找node为to的键 + arr.fullColumn(id).forEach((fromNodeId, relation) -> { + // 在上一次遍历中,fromNodeId为id, + if (fromNodeId == id) { + return; + } + res.add(new OuterEntry<>(nodes.peek(fromNodeId), node, relation)); + arr.remove(fromNodeId, id); + }); + nodes.takeOut(id); + return res; + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + return arr.add( + findNodeId(Objects.requireNonNull(fromNode), true), + findNodeId(Objects.requireNonNull(toNode), true), + Objects.requireNonNull(relation) + ); + } + + @Override + public Set> getRelationFrom(N from) { + int id = findNodeId(Objects.requireNonNull(from), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为from的键 + arr.fullLine(id).forEach((toNodeId, relation) -> res.add(new OuterEntry<>(from, nodes.peek(toNodeId), relation))); + return res; + } + + @Override + public Set> getRelationTo(N to) { + int id = findNodeId(Objects.requireNonNull(to), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为to的键 + arr.fullColumn(id).forEach((fromNodeId, relation) -> + res.add(new OuterEntry<>(nodes.peek(fromNodeId), to, relation))); + return res; + } + + @Override + public Set nodesView() { + return new AbstractNodesView() { + @Override + public Iterator iterator() { + return nodes.stream().map(Map.Entry::getValue).iterator(); + } + + @Override + public int size() { + return nodes.size(); + } + }; + } + + @Override + public Set> getRelations() { + return arr.stream().map((Function, Entry>) rPoint -> new OuterEntry<>( + nodes.peek(rPoint.getLine()), + nodes.peek(rPoint.getColumn()), + rPoint.getElement() + )).collect(Collectors.toSet()); + } + + private int findNodeId(N node, boolean mustExistElseThrowEx) { + int id = nodes.findId(Objects.requireNonNull(node)); + if (mustExistElseThrowEx && id < 0) { + throw new IllegalArgumentException("No node exists : " + node); + } + return id; + } + + private static class OuterEntry extends AbstractEntry { + private final N from; + private final N to; + private final R relation; + + public OuterEntry(N from, N to, R relation) { + this.from = from; + this.to = to; + this.relation = relation; + } + + @Override + public N getFrom() { + return from; + } + + @Override + public N getTo() { + return to; + } + + @Override + public R getRelation() { + return relation; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java new file mode 100644 index 0000000..ae05f6f --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java @@ -0,0 +1,159 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.*; + +/** + * 自动扩容的id储物柜,线程不安全。 + * + * @author create by TcSnZh on 2021/5/13-下午1:24 + */ +public class CommonStoreArk extends AbstractStoreArk { + private Object[] elements; + + /** + * 已经分配的下标数 + */ + private int allocSize = 0; + + /** + * 保存着最小空元素的队列 + */ + private final Queue emptyPoints = new PriorityQueue<>(Integer::compareTo); + + public CommonStoreArk(int initialCapacity) { + elements = new Object[initialCapacity]; + } + + public CommonStoreArk() { + this(10); + } + + @Override + public int store(E element) { + int id; + elements[id = pollId()] = element; + return id; + } + + @Override + public E peek(int id) { + if (id < 0) { + throw new IllegalArgumentException("id " + id + " can't be negative"); + } + if (id >= elements.length) { + return null; + } + //noinspection unchecked + return (E) elements[id]; + } + + @Override + public E takeOut(int id) { + if (id < 0) { + throw new IllegalArgumentException("id " + id + " can't be negative"); + } + if (id >= elements.length) { + return null; + } + //noinspection unchecked + E out = (E) elements[id]; + elements[id] = null; + if (id == allocSize - 1) { + allocSize--; + } else { + emptyPoints.add(id); + } + return out; + } + + @Override + public int size() { + return allocSize - emptyPoints.size(); + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + private final Map.Entry[] items; + + private int idx = 0; + + { + //noinspection unchecked + items = new Map.Entry[size()]; + int itemsIdx = 0; + Iterator emptyPointItr = emptyPoints.iterator(); + for (int i = 0; i < allocSize; i++) { + Object element = elements[i]; + if (element == null) { + continue; + } + final int _i = i; + //noinspection unchecked + items[itemsIdx++] = new Map.Entry() { + private final int k = _i; + private E v = (E) element; + + @Override + public Integer getKey() { + return k; + } + + @Override + public E getValue() { + return v; + } + + @Override + public E setValue(E value) { + E _v = this.v; + this.v = value; + return _v; + } + + @Override + public String toString() { + return "{" + k + ':' + v + '}'; + } + }; + } + } + + + @Override + public boolean hasNext() { + return idx < items.length; + } + + @Override + public Map.Entry next() { + return items[idx++]; + } + }; + } + + @Override + public int findId(E element) { + int i = 0; + for (Object o : elements) { + if (Objects.equals(o, element)) { + return i; + } + i++; + } + return -1; + } + + private int pollId() { + if (!emptyPoints.isEmpty()) { + return emptyPoints.poll(); + } + int id = allocSize++; + int length = elements.length; + if (id >= length) { + // 扩容 + elements = Arrays.copyOf(elements, Math.max(length + 1, length + (length >> 1))); + } + return id; + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java new file mode 100644 index 0000000..4f326d2 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java @@ -0,0 +1,185 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.AbstractSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +/** + * @author create by TcSnZh on 2021/5/16-下午11:27 + */ +public interface DirectedGraph extends Graph { + @Override + default boolean isDirected() { + return true; + } + + static DirectedGraph readOnlyDigraph(DirectedGraph source) { + return new ReadOnlyDirectedGraph<>(source); + } + + static DirectedGraph synchronizedDigraph(DirectedGraph source) { + return synchronizedDigraph(source, new Object()); + } + + static DirectedGraph synchronizedDigraph(DirectedGraph source, Object mutex) { + return new SyncDirectedGraph<>(source, mutex); + } + + class ReadOnlyDirectedGraph extends AbstractDirectedGraph { + private final DirectedGraph source; + + public ReadOnlyDirectedGraph(DirectedGraph source) { + this.source = source; + } + + private static UnsupportedOperationException readOnlyGraph() { + return new UnsupportedOperationException("readOnly graph"); + } + + @Override + public boolean addNode(N node) { + throw readOnlyGraph(); + } + + @Override + public boolean containsNode(N node) { + return source.containsNode(node); + } + + @Override + public Set> removeNode(N node) { + throw readOnlyGraph(); + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + throw readOnlyGraph(); + } + + @Override + public Set> getRelationFrom(N from) { + return source.getRelationFrom(from); + } + + @Override + public Set> getRelationTo(N to) { + return source.getRelationTo(to); + } + + @Override + public Set nodesView() { + return new AbstractSet() { + private final Set nodesViewSource = source.nodesView(); + + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator iteratorSource = nodesViewSource.iterator(); + + @Override + public boolean hasNext() { + return iteratorSource.hasNext(); + } + + @Override + public N next() { + return iteratorSource.next(); + } + + @Override + public void remove() { + throw readOnlyGraph(); + } + }; + } + + @Override + public int size() { + return nodesViewSource.size(); + } + + @Override + public boolean add(N n) { + throw readOnlyGraph(); + } + + @Override + public boolean remove(Object o) { + throw readOnlyGraph(); + } + }; + } + + @Override + public Set> getRelations() { + return source.getRelations(); + } + } + + class SyncDirectedGraph extends AbstractDirectedGraph { + private final DirectedGraph source; + private final Object mutex; + + public SyncDirectedGraph(DirectedGraph source, Object mutex) { + this.source = source; + this.mutex = mutex; + } + + @Override + public boolean addNode(N node) { + synchronized (mutex) { + return source.addNode(node); + } + } + + @Override + public boolean containsNode(N node) { + synchronized (mutex) { + return source.containsNode(node); + } + } + + @Override + public Set> removeNode(N node) { + synchronized (mutex) { + return source.removeNode(node); + } + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + synchronized (mutex) { + return source.putRelation(fromNode, relation, toNode); + } + } + + @Override + public Set> getRelationFrom(N from) { + synchronized (mutex) { + return source.getRelationFrom(from); + } + } + + @Override + public Set> getRelationTo(N to) { + synchronized (mutex) { + return source.getRelationTo(to); + } + } + + @Override + public Set nodesView() { + synchronized (mutex) { + return Collections.synchronizedSet(source.nodesView()); + } + } + + @Override + public Set> getRelations() { + synchronized (mutex) { + return source.getRelations(); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java new file mode 100644 index 0000000..e9903ff --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java @@ -0,0 +1,106 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Set; + +/** + * 图数据结构 + * + * @author create by TcSnZh on 2021/5/13-上午11:37 + */ +@SuppressWarnings("unused") +public interface Graph { + /** + * 添加节点。 + * 如果节点已经存在,则不会添加。 + * + * @param node 添加进图的节点 + * @return 添加成功返回true,如果节点已经存在返回false。 + */ + boolean addNode(N node); + + /** + * 添加一堆Node,任一成功返回true + */ + default boolean addNode(N... nodes) { + boolean success = false; + for (N node : nodes) { + if (addNode(node)) { + success = true; + } + } + return success; + } + + /** + * 是否存在节点 + * + * @param node 节点。 + * @return 存在返回true,否则返回false。 + */ + boolean containsNode(N node); + + /** + * 移除节点。 + * 返回与该节点有关系的,被一并移出的键。 + * + * @param node 节点 + * @return 返回值不会为null。 + * @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出异常。 + */ + Set> removeNode(N node); + + /** + * 添加关系 + * 在无向图中fromNode与toNode参数的位置调换没有影响。 + * + * @param fromNode 从这个节点开始 + * @param relation 关系 + * @param toNode 以那个节点为目标 + * @return 如果之前存在关系,则会替换之前的关系,返回出被替换的之前存在的关系。如果之前没有关系,返回null。 + * @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出该异常。 + */ + R putRelation(N fromNode, R relation, N toNode); + + /** + * 获取“从这个节点开始”的所有关系 + * + * @param from 关系开始的节点 + * @return 返回 {@link Entry}键。 + */ + Set> getRelationFrom(N from); + + /** + * 获取“以这个节点为目标”的所有关系 + * + * @param to 被关系的节点 + * @return 返回 {@link Entry}键。 + */ + Set> getRelationTo(N to); + + /** + * 返回全部节点视图 + * + * @return 视图 + */ + Set nodesView(); + + /** + * 返回全部关系,返回的是新Set + * + * @return 与本类无关的Set + */ + Set> getRelations(); + + /** + * 是否有向 + */ + boolean isDirected(); + + interface Entry { + N getFrom(); + + N getTo(); + + R getRelation(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java new file mode 100644 index 0000000..39dee10 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java @@ -0,0 +1,230 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 稀疏二维数组。 + *

+ * 可以设置是否允许存入null。 + * + * @author create by TcSnZh on 2021/5/14-下午9:45 + */ +public class SparseArray2D extends AbstractArray2D { + + // ========== properties ========== + + /** + * 限制长宽,默认为Integer.MAX_VALUE。稀疏数组不在乎这些。 + */ + private final int maxLineLength; + private final int maxColumnLength; + private final boolean allowNull; + + private final Map items = new HashMap<>(); + + // ========== index cache properties ========== + + /** + * 缓存行列索引 + */ + private final NavigableMap> indexOfLine2columns = new TreeMap<>(Integer::compareTo); + private final NavigableMap> indexOfColumn2lines = new TreeMap<>(Integer::compareTo); + + // ========== constructor ========== + + public SparseArray2D() { + this(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public SparseArray2D(boolean allowNull) { + this(Integer.MAX_VALUE, Integer.MAX_VALUE, allowNull); + } + + public SparseArray2D(int maxLineCapacity, int maxColumnCapacity) { + this(maxLineCapacity, maxColumnCapacity, false); + } + + public SparseArray2D(int maxLineCapacity, int maxColumnCapacity, boolean allowNull) { + this.maxLineLength = maxLineCapacity; + this.maxColumnLength = maxColumnCapacity; + this.allowNull = allowNull; + } + + // ========== public methods ========== + @Override + public int lineLength() { + return maxLineLength; + } + + @Override + public int columnLength() { + return maxColumnLength; + } + + @Override + public E add(int line, int column, E element) { + if (!allowNull && element == null) { + throw new NullPointerException("null is not allowed"); + } + Object put = items.put(BiInt.of(checkLine(line), checkColumn(column)), element == null ? NULL : element); + addIndex(line, column); + //noinspection unchecked + return NULL.equals(put) ? null : (E) put; + } + + @Override + public E remove(int line, int column) { + BiInt idx = BiInt.of(checkLine(line), checkColumn(column)); + Object get = items.get(idx); + if (get == null) { + throw new IllegalArgumentException("There is no element in line " + line + " column " + column); + } + items.remove(idx); + removeIndex(line, column); + //noinspection unchecked + return NULL.equals(get) ? null : (E) get; + } + + /** + * 该方法如果返回null,则分不清 之前存入了null 还是 没有存入过 + *

+ * {@inheritDoc} + */ + @Override + public E get(int line, int column) { + Object get = items.get(BiInt.of(checkLine(line), checkColumn(column))); + //noinspection unchecked + return NULL.equals(get) ? null : (E) get; + } + + @Override + public boolean containsElement(E element) { + if (NULL.equals(element)) { + if (!allowNull) { + return false; + } + return items.values().stream().anyMatch(v -> NULL.equals(element)); + } + return items.values().stream().anyMatch(element::equals); + } + + @Override + public Map fullLine(int line) { + return Optional.ofNullable(indexOfLine2columns.get(line)) + .map(set -> set.stream() + .collect(Collectors.toMap(column -> column, column -> { + //noinspection unchecked + return (E) items.get(BiInt.of(line, column)); + }))) + .orElse(Collections.emptyMap()); + } + + @Override + public Map fullColumn(int column) { + return Optional.ofNullable(indexOfColumn2lines.get(column)) + .map(set -> set.stream() + .collect(Collectors.toMap(line -> line, line -> { + //noinspection unchecked + return (E) items.get(BiInt.of(line, column)); + }))) + .orElse(Collections.emptyMap()); + } + + @Override + public Iterator> iterator(Comparator foreachOrder) { + return new Iterator>() { + private final Iterator> it; + private Point last = null; + private boolean removed = false; + + { + it = items.entrySet().stream() + .sorted((o1, o2) -> foreachOrder.compare(o1.getKey(), o2.getKey())) + .iterator(); + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Point next() { + Map.Entry next = it.next(); + removed = false; + Object v = next.getValue(); + //noinspection unchecked + return last = new PointImpl<>(next.getKey(), NULL.equals(v) ? null : (E) v); + } + + @Override + public void remove() { + if (last == null || removed) { + throw new IllegalStateException(last == null + ? "Iterator has not yet been called .next() ." + : "Iterator item already removed : " + last); + } + BiInt idx = last.getIdx(); + SparseArray2D.this.remove(idx.getM(), idx.getN()); + } + }; + } + + // ========== private methods ========== + + private int checkLine(int line) { + int len = lineLength(); + if (line < 0 || line >= len) { + throw new IndexOutOfBoundsException("Line " + line + " out of bound [0," + (len - 1) + "]"); + } + return line; + } + + private int checkColumn(int column) { + int len = columnLength(); + if (column < 0 || column >= len) { + throw new IndexOutOfBoundsException("Column " + column + " out of bound [0," + (len - 1) + "]"); + } + return column; + } + + private void addIndex(int line, int column) { + indexOfLine2columns.computeIfAbsent(line, line1 -> new TreeSet<>(Integer::compareTo)).add(column); + indexOfColumn2lines.computeIfAbsent(column, column1 -> new TreeSet<>(Integer::compareTo)).add(line); + + } + + private void removeIndex(int line, int column) { + // remove line index + { + NavigableSet columns = indexOfLine2columns.get(line); + if (columns == null || !columns.contains(column)) { + throw new ConcurrentModificationException( + "线程不安全导致索引异常 : lines " + columns + " is null or not contain line " + line); + + } + if (columns.size() == 1) { + indexOfLine2columns.remove(line); + } else { + columns.remove(column); + } + } + // remove column index + { + NavigableSet lines = indexOfColumn2lines.get(column); + if (lines == null || !lines.contains(line)) { + throw new ConcurrentModificationException( + "线程不安全导致索引异常 : lines " + lines + " is null or not contain column " + column); + } + if (lines.size() == 1) { + indexOfColumn2lines.remove(column); + } else { + lines.remove(column); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java new file mode 100644 index 0000000..063413c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java @@ -0,0 +1,69 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Map; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * id存储柜。 + * 每个元素的id是固定的(除非取出后重新加入),且id是大于等于0,且分配到的id必须是未分配的id中最小的。 + *

+ * 类似于我们去游泳馆,里面的存放个人物品的柜子。 + * 放进去元素后,会分配一个id。然后凭借该id取出元素。 + * 不过不同于这些现实中的柜子的是,这个存储柜必定会提供最小的id,并且必定>0。 + *

+ * + * @author create by TcSnZh on 2021/5/14-上午2:29 + */ +public interface StoreArk extends Iterable> { + /** + * 存入元素 + * + * @param element 元素。 + * @return 返回最小的id。从0开始。 + */ + int store(E element); + + /** + * 查看元素 + * + * @param id id; + * @return 返回存在的元素。如果本id未被占用 或 原先存入null,返回null。 + * @throws IllegalArgumentException id为负数时抛出该异常 + */ + E peek(int id); + + /** + * 取出元素 + * + * @param id id + * @return 返回被取出的元素。如果本id未被占用 或 原先存入null,返回null。 + * @throws IllegalArgumentException id为负数时抛出该异常 + */ + E takeOut(int id); + + /** + * 元素个数 + */ + int size(); + + /** + * 是否为空 + */ + boolean isEmpty(); + + /** + * 查找元素的id + * + * @param element 元素 + * @return 如果存在,返回id。不存在返回-1 + */ + int findId(E element); + + /** + * 返回流 + */ + default Stream> stream() { + return StreamSupport.stream(spliterator(), false); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java new file mode 100644 index 0000000..b3d6ec3 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java @@ -0,0 +1,16 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Collection; + +/** + * @author create by TcSnZh on 2021/5/15-下午7:58 + */ +public interface Tree { + interface Node { + default Node getParent(){ + throw new UnsupportedOperationException("Get parent node is not supported."); + } + + Collection getChildren(); + } +} diff --git a/src/main/java/com/jd/platform/async/util/collection/WheelIterator.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java similarity index 87% rename from src/main/java/com/jd/platform/async/util/collection/WheelIterator.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java index 97ea47d..0c9ad40 100644 --- a/src/main/java/com/jd/platform/async/util/collection/WheelIterator.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.util.collection; +package com.jd.platform.async.openutil.collection; import java.util.Iterator; diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java new file mode 100644 index 0000000..2d92fe2 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java @@ -0,0 +1,46 @@ +package com.jd.platform.async.openutil.concurrent; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** + * 范围锁 todo + * + * @author create by TcSnZh on 2021/5/15-下午6:23 + */ +public interface RangeLock extends Lock { + void lock(int start, int end); + + boolean tryLock(int start, int end); + + boolean tryLock(int start, int end, long time, TimeUnit unit) throws InterruptedException; + + void lockInterruptibly(int start, int end) throws InterruptedException; + + @Override + Condition newCondition(); + + @Override + void unlock(); + + @Override + default void lock() { + lock(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + @Override + default void lockInterruptibly() throws InterruptedException { + lockInterruptibly(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + @Override + default boolean tryLock() { + return tryLock(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + @Override + default boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + return tryLock(Integer.MIN_VALUE, Integer.MAX_VALUE, time, unit); + } +} diff --git a/src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java similarity index 82% rename from src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java index cc2d711..d26fa1e 100644 --- a/src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.util.timer; +package com.jd.platform.async.openutil.timer; /** * @author create by TcSnZh on 2021/5/12-下午6:36 @@ -10,6 +10,7 @@ public abstract class AbstractWheelTimer implements Timer, AutoCloseable { public abstract void start(); + @SuppressWarnings("RedundantThrows") @Override public void close() throws Exception { stop(); diff --git a/src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java similarity index 93% rename from src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java index 864e9cd..90df6e9 100644 --- a/src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.util.timer; +package com.jd.platform.async.openutil.timer; import java.util.*; import java.util.concurrent.*; @@ -10,12 +10,11 @@ import java.util.concurrent.atomic.AtomicLong; * 从netty里抄来的,删去了一些功能。 *

* - * 如果违反开源协议,请联系作者: zh0u.he@qq.com + * 如果违反开源协议,请联系作者: zh.jobs@foxmail.com * If violate the open source agreement, please contact the author : zh0u.he@qq.com * * * @author create by TcSnZh on 2021/5/12-下午7:16 - * @ */ public class HashedWheelTimer extends AbstractWheelTimer { @@ -42,6 +41,7 @@ public class HashedWheelTimer extends AbstractWheelTimer { * ({@link Executors#defaultThreadFactory()}), default tick duration, and * default number of ticks per wheel. */ + @SuppressWarnings("unused") public HashedWheelTimer() { this(Executors.defaultThreadFactory()); } @@ -56,6 +56,7 @@ public class HashedWheelTimer extends AbstractWheelTimer { * @throws NullPointerException if {@code unit} is {@code null} * @throws IllegalArgumentException if {@code tickDuration} is <= 0 */ + @SuppressWarnings("unused") public HashedWheelTimer(long tickDuration, TimeUnit unit) { this(Executors.defaultThreadFactory(), tickDuration, unit); } @@ -70,6 +71,7 @@ public class HashedWheelTimer extends AbstractWheelTimer { * @throws NullPointerException if {@code unit} is {@code null} * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 */ + @SuppressWarnings("unused") public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) { this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel); } @@ -118,28 +120,7 @@ public class HashedWheelTimer extends AbstractWheelTimer { public HashedWheelTimer( ThreadFactory threadFactory, long tickDuration, TimeUnit unit, int ticksPerWheel) { - this(threadFactory, tickDuration, unit, ticksPerWheel, true); - } - - /** - * Creates a new timer. - * - * @param threadFactory a {@link ThreadFactory} that creates a - * background {@link Thread} which is dedicated to - * {@link TimerTask} execution. - * @param tickDuration the duration between tick - * @param unit the time unit of the {@code tickDuration} - * @param ticksPerWheel the size of the wheel - * @param leakDetection {@code true} if leak detection should be enabled always, - * if false it will only be enabled if the worker thread is not - * a daemon thread. - * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} - * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 - */ - public HashedWheelTimer( - ThreadFactory threadFactory, - long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection) { - this(threadFactory, tickDuration, unit, ticksPerWheel, leakDetection, -1); + this(threadFactory, tickDuration, unit, ticksPerWheel, -1); } /** @@ -151,21 +132,19 @@ public class HashedWheelTimer extends AbstractWheelTimer { * @param tickDuration the duration between tick * @param unit the time unit of the {@code tickDuration} * @param ticksPerWheel the size of the wheel - * @param leakDetection {@code true} if leak detection should be enabled always, - * if false it will only be enabled if the worker thread is not - * a daemon thread. * @param maxPendingTimeouts The maximum number of pending timeouts after which call to * {@code newTimeout} will result in - * {@link java.util.concurrent.RejectedExecutionException} + * {@link RejectedExecutionException} * being thrown. No maximum pending timeouts limit is assumed if * this value is 0 or negative. * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 */ - public HashedWheelTimer( - ThreadFactory threadFactory, - long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection, - long maxPendingTimeouts) { + public HashedWheelTimer(ThreadFactory threadFactory, + long tickDuration, + TimeUnit unit, + int ticksPerWheel, + long maxPendingTimeouts) { Objects.requireNonNull(threadFactory, "threadFactory must not null !"); Objects.requireNonNull(threadFactory, "unit must not null !"); @@ -427,6 +406,7 @@ public class HashedWheelTimer extends AbstractWheelTimer { } try { + //noinspection BusyWait Thread.sleep(sleepTimeMs); } catch (InterruptedException ignored) { if (workerState.get() == WORKER_STATE_SHUTDOWN) { diff --git a/src/main/java/com/jd/platform/async/util/timer/Timeout.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java similarity index 91% rename from src/main/java/com/jd/platform/async/util/timer/Timeout.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java index 1bbc9b1..84e875c 100644 --- a/src/main/java/com/jd/platform/async/util/timer/Timeout.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.util.timer; +package com.jd.platform.async.openutil.timer; /** * 借鉴netty。 @@ -32,5 +32,6 @@ public interface Timeout { * * @return 如果取消成功完成,则为true,否则为false */ + @SuppressWarnings("unused") boolean cancel(); } diff --git a/src/main/java/com/jd/platform/async/util/timer/Timer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java similarity index 87% rename from src/main/java/com/jd/platform/async/util/timer/Timer.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java index 3ab2052..17870c1 100644 --- a/src/main/java/com/jd/platform/async/util/timer/Timer.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java @@ -1,7 +1,8 @@ -package com.jd.platform.async.util.timer; +package com.jd.platform.async.openutil.timer; import java.util.Set; -import java.util.concurrent.*; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** @@ -23,6 +24,7 @@ public interface Timer { */ Timeout newTimeout(TimerTask task, long delay, TimeUnit unit); + @SuppressWarnings("unused") default Timeout newTimeout(Runnable runnable, long delay, TimeUnit unit) { AtomicReference timeoutRef = new AtomicReference<>(); newTimeout(timeout -> { diff --git a/src/main/java/com/jd/platform/async/util/timer/TimerTask.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java similarity index 79% rename from src/main/java/com/jd/platform/async/util/timer/TimerTask.java rename to asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java index 6ea3c76..047590c 100644 --- a/src/main/java/com/jd/platform/async/util/timer/TimerTask.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.util.timer; +package com.jd.platform.async.openutil.timer; /** * 类似于netty的TimerTask。 diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java new file mode 100644 index 0000000..35ecf2a --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java @@ -0,0 +1,33 @@ +package com.jd.platform.async.openutil.unsafe; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +/** + * @author create by TcSnZh on 2021/5/16-下午4:36 + */ +@SuppressWarnings({"AlibabaAbstractClassShouldStartWithAbstractNaming", "unused"}) +abstract class UnsafeUtil { + private static final Unsafe unsafe; + + static { + Field theUnsafe; + try { + theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + } catch (NoSuchFieldException e) { + throw new Error(e); + } + theUnsafe.setAccessible(true); + try { + unsafe = (Unsafe) theUnsafe.get(null); + } catch (IllegalAccessException e) { + throw new Error(e); + } + } + + @SuppressWarnings("FinalStaticMethod") + public static final Unsafe getUnsafe() { + return unsafe; + } +} diff --git a/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java b/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java new file mode 100644 index 0000000..c212ad3 --- /dev/null +++ b/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java @@ -0,0 +1,51 @@ +package openutiltest; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 便于测试的jdk动态代理 + * + * @author create by TcSnZh on 2021/5/16-下午11:38 + */ +public class PrintProxy { + public PrintProxy(Class clazz) { + this.interfaceClazz = clazz; + } + + private final Class interfaceClazz; + + public I proxyTo(I obj, String objNickName) { + //noinspection unchecked + return (I) Proxy.newProxyInstance( + obj.getClass().getClassLoader(), + new Class[]{interfaceClazz}, + (proxy, method, args) -> { + String methodInfo = methodInfo(method); + try { + Object res = method.invoke(obj, args); + System.out.printf(objNickName + " 执行方法: %-40s --> 方法返回值: %-20s --> this.toString() = %-40s\n", + methodInfo, res, obj); + return res; + } catch (Exception e) { + System.err.printf(objNickName + " 执行方法: %-40s --> 异常信息: %-40s --> this.toString() = %-40s\n", + methodInfo, e.getClass().getSimpleName() + " : " + e.getMessage(), obj + ); + throw e; + } + } + ); + } + + private static String methodInfo(Method method) { + StringBuilder sb = new StringBuilder().append(method.getName()).append('('); + for (Class parameterType : method.getParameterTypes()) { + sb.append(parameterType.getSimpleName()).append(", "); + } + if (method.getParameterTypes().length > 0) { + sb.delete(sb.length() - 2, sb.length()); + } + return sb.append(')').toString(); + } + +} diff --git a/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java new file mode 100644 index 0000000..533485b --- /dev/null +++ b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java @@ -0,0 +1,37 @@ +package openutiltest; + +import com.jd.platform.async.openutil.collection.CommonDirectedGraph; +import com.jd.platform.async.openutil.collection.DirectedGraph; + +import java.util.Arrays; + +/** + * 测试图工具类的使用 + * + * @author create by TcSnZh on 2021/5/16-下午11:25 + */ +public class TestGraph { + public static void main(String[] args) { + test_CommonDirectedGraph(); + } + + private static void test_CommonDirectedGraph() { + System.out.println("\n\n ==================== 测试正常使用 =================="); + //noinspection unchecked + DirectedGraph graph = + new PrintProxy<>(DirectedGraph.class).proxyTo(new CommonDirectedGraph<>(), "graph"); + graph.isDirected(); + graph.addNode("胖虎"); + graph.addNode("大雄"); + graph.putRelation("胖虎", "打", "大雄"); + graph.addNode("静香"); + graph.nodesView().addAll(Arrays.asList("小夫", "胖虎的妹妹", "哆啦A梦")); + graph.putRelation("胖虎", "是其哥", "胖虎的妹妹"); + graph.putRelation("胖虎的妹妹", "是其妹", "胖虎"); + graph.putRelation("胖虎的妹妹", "喜欢", "大雄"); + graph.putRelation("胖虎", "????", "小夫"); + graph.putRelation("大雄", "喜欢", "静香"); + graph.removeNode("大雄"); + graph.getRelations(); + } +} diff --git a/asyncTool-scheduling/pom.xml b/asyncTool-scheduling/pom.xml new file mode 100644 index 0000000..7561f8d --- /dev/null +++ b/asyncTool-scheduling/pom.xml @@ -0,0 +1,31 @@ + + + + asyncTool + com.jd.platform + 1.5.0-SNAPSHOT + + 4.0.0 + + asyncTool-scheduling + + + 8 + 8 + + + + + asyncTool-core + com.jd.platform + 1.5.0-SNAPSHOT + + + com.alibaba + fastjson + 1.2.76 + + + \ No newline at end of file diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java new file mode 100644 index 0000000..47cbbc6 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java @@ -0,0 +1,40 @@ +package com.jd.platform.async.scheduling; + +import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; + +/** + * @author create by TcSnZh on 2021/5/17-上午1:22 + */ +public class DefaultSchedulingJsonParser implements SchedulingJsonParser { + + + // ========== singleton instance ========== + + /** + * 私有构造方法,需要通过{@link #getInstance()}方法获取单例。 + */ + private DefaultSchedulingJsonParser() { + } + + /** + * 获取单例 + */ + public static DefaultSchedulingJsonParser getInstance() { + return instance; + } + + private static final DefaultSchedulingJsonParser instance = new DefaultSchedulingJsonParser(); + + // ========== public methods ========== + + @Override + public SchedulingDrawingsModel parse(String json) { + + + + // todo + return null; + } + + // ========== util methods ========== +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java new file mode 100644 index 0000000..49c7c7d --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java @@ -0,0 +1,35 @@ +package com.jd.platform.async.scheduling; + +import sun.misc.Unsafe; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; + +/** + * 调度工厂。传入图纸生成一组wrapper。 + * + * @author create by TcSnZh on 2021/5/17-上午1:11 + */ +public class SchedulingFactory { + private final String factoryName; + + /** + * 无参构造,默认使用 {@code 栈信息<自增long值> } 作为工厂名 + */ + public SchedulingFactory() { + this(Thread.currentThread().getStackTrace()[2] + "<" + defaultFactoryNameCount.getAndIncrement() + ">"); + } + + /** + * 指定工厂名 + * + * @param factoryName 工厂名 + */ + public SchedulingFactory(String factoryName) { + this.factoryName = factoryName; + } + + // ========== static ========== + + private static final AtomicLong defaultFactoryNameCount = new AtomicLong(); +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java new file mode 100644 index 0000000..ee4b908 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java @@ -0,0 +1,24 @@ +package com.jd.platform.async.scheduling; + +import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; + +/** + * @author create by TcSnZh on 2021/5/17-下午7:22 + */ +public interface SchedulingJsonParser { + + /** + * 解析json为图纸对象 + * + * @param json json + * @return 返回图纸对象接口 + */ + SchedulingDrawingsModel parse(String json); + + /** + * 默认实现 + */ + static SchedulingJsonParser getDefaultInstance() { + return DefaultSchedulingJsonParser.getInstance(); + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java new file mode 100644 index 0000000..21b55e6 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java @@ -0,0 +1,25 @@ +package com.jd.platform.async.scheduling.exception; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:20 + */ +public class IllegalConfigException extends Exception{ + public IllegalConfigException() { + } + + public IllegalConfigException(String message) { + super(message); + } + + public IllegalConfigException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalConfigException(Throwable cause) { + super(cause); + } + + public IllegalConfigException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java new file mode 100644 index 0000000..6171899 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java @@ -0,0 +1,80 @@ +package com.jd.platform.async.scheduling.model; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:51 + */ +@SuppressWarnings("unused") +public class ObjectModel { + protected String constObjectName; + protected String className; + protected Long sameObjectId; + protected Map properties; + + public ObjectModel() { + } + + public String getConstObjectName() { + return constObjectName; + } + + public void setConstObjectName(String constObjectName) { + this.constObjectName = constObjectName; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public Long getSameObjectId() { + return sameObjectId; + } + + public void setSameObjectId(Long sameObjectId) { + this.sameObjectId = sameObjectId; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public static Map getConstObjects() { + return constObjects; + } + + // static constants + + private static final Map constObjects; + + static { + constObjects = new HashMap<>(16); + constObjects.put("NOT_SKIP", SkipStrategy.NOT_SKIP); + constObjects.put("CHECK_ONE_LEVEL", SkipStrategy.CHECK_ONE_LEVEL); + constObjects.put("ALL_DEPENDENCIES_ALL_SUCCESS", DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS); + constObjects.put("ALL_DEPENDENCIES_ANY_SUCCESS", DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS); + constObjects.put("ALL_DEPENDENCIES_NONE_FAILED", DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED); + constObjects.put("SUCCESS_CONTINUE", DependOnUpWrapperStrategy.SUCCESS_CONTINUE); + constObjects.put("SUCCESS_START_INIT_CONTINUE", DependOnUpWrapperStrategy.SUCCESS_START_INIT_CONTINUE); + constObjects.put("PRINT_EXCEPTION_STACK_TRACE", ICallback.PRINT_EXCEPTION_STACK_TRACE); + } + + public static T getConstObject(String name) { + //noinspection unchecked + return (T) constObjects.get(name); + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java new file mode 100644 index 0000000..1e00e68 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java @@ -0,0 +1,255 @@ +package com.jd.platform.async.scheduling.model; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 工厂图纸模型对象。 + * 具体参数含义,请查阅 QuickStart.md + * + * @author create by TcSnZh on 2021/5/17-上午1:16 + */ +@SuppressWarnings("unused") +public class SchedulingDrawingsModel { + protected String drawingsName; + protected List wrappers; + protected List relations; + protected BeginWorkModel beginWork; + + public static class WrapperModel { + protected String id; + protected ParamModel param; + protected ObjectModel worker; + protected ObjectModel callback; + protected WrapperStrategyModel wrapperStrategy; + protected Boolean allowInterrupt; + protected Boolean enableTimeout; + protected Long timeoutLength; + protected TimeUnit timeUnit; + protected String extendConfig; + + public static class ParamModel { + protected Boolean useObjectModel; + protected Object value; // true - ObjectModel ; false - the json converted to type, such as Map\List\String... + + public Boolean getUseObjectModel() { + return useObjectModel; + } + + public void setUseObjectModel(Boolean useObjectModel) { + this.useObjectModel = useObjectModel; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + } + + public static class WrapperStrategyModel { + protected Map dependOnUpWrapperStrategyMapper; + protected ObjectModel dependenceStrategy; + protected ObjectModel skipStrategy; + + public Map getDependOnUpWrapperStrategyMapper() { + return dependOnUpWrapperStrategyMapper; + } + + public void setDependOnUpWrapperStrategyMapper(Map dependOnUpWrapperStrategyMapper) { + this.dependOnUpWrapperStrategyMapper = dependOnUpWrapperStrategyMapper; + } + + public ObjectModel getDependenceStrategy() { + return dependenceStrategy; + } + + public void setDependenceStrategy(ObjectModel dependenceStrategy) { + this.dependenceStrategy = dependenceStrategy; + } + + public ObjectModel getSkipStrategy() { + return skipStrategy; + } + + public void setSkipStrategy(ObjectModel skipStrategy) { + this.skipStrategy = skipStrategy; + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ParamModel getParam() { + return param; + } + + public void setParam(ParamModel param) { + this.param = param; + } + + public ObjectModel getWorker() { + return worker; + } + + public void setWorker(ObjectModel worker) { + this.worker = worker; + } + + public ObjectModel getCallback() { + return callback; + } + + public void setCallback(ObjectModel callback) { + this.callback = callback; + } + + public WrapperStrategyModel getWrapperStrategy() { + return wrapperStrategy; + } + + public void setWrapperStrategy(WrapperStrategyModel wrapperStrategy) { + this.wrapperStrategy = wrapperStrategy; + } + + public Boolean getAllowInterrupt() { + return allowInterrupt; + } + + public void setAllowInterrupt(Boolean allowInterrupt) { + this.allowInterrupt = allowInterrupt; + } + + public Boolean getEnableTimeout() { + return enableTimeout; + } + + public void setEnableTimeout(Boolean enableTimeout) { + this.enableTimeout = enableTimeout; + } + + public Long getTimeoutLength() { + return timeoutLength; + } + + public void setTimeoutLength(Long timeoutLength) { + this.timeoutLength = timeoutLength; + } + + public TimeUnit getTimeUnit() { + return timeUnit; + } + + public void setTimeUnit(TimeUnit timeUnit) { + this.timeUnit = timeUnit; + } + + public String getExtendConfig() { + return extendConfig; + } + + public void setExtendConfig(String extendConfig) { + this.extendConfig = extendConfig; + } + } + + public static class RelationModel { + protected Object from; // from和to最多有一个数组,剩下的都是String + protected Object to; + + public Object getFrom() { + return from; + } + + public void setFrom(Object from) { + this.from = from; + } + + public Object getTo() { + return to; + } + + public void setTo(Object to) { + this.to = to; + } + } + + public static class BeginWorkModel { + protected Long timeoutLength; + protected TimeUnit timeoutUnit; + protected List wrappers; + protected String executor; + + public Long getTimeoutLength() { + return timeoutLength; + } + + public void setTimeoutLength(Long timeoutLength) { + this.timeoutLength = timeoutLength; + } + + public TimeUnit getTimeoutUnit() { + return timeoutUnit; + } + + public void setTimeoutUnit(TimeUnit timeoutUnit) { + this.timeoutUnit = timeoutUnit; + } + + public List getWrappers() { + return wrappers; + } + + public void setWrappers(List wrappers) { + this.wrappers = wrappers; + } + + public String getExecutor() { + return executor; + } + + public void setExecutor(String executor) { + this.executor = executor; + } + } + + public String getDrawingsName() { + return drawingsName; + } + + public void setDrawingsName(String drawingsName) { + this.drawingsName = drawingsName; + } + + public List getWrappers() { + return wrappers; + } + + public void setWrappers(List wrappers) { + this.wrappers = wrappers; + } + + public List getRelations() { + return relations; + } + + public void setRelations(List relations) { + this.relations = relations; + } + + public BeginWorkModel getBeginWork() { + return beginWork; + } + + public void setBeginWork(BeginWorkModel beginWork) { + this.beginWork = beginWork; + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java new file mode 100644 index 0000000..cdd7b97 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java @@ -0,0 +1,16 @@ +package com.jd.platform.async.scheduling.util; + +/** + * 反射工具类 + * + * @author tcsnzh[zh.jobs@foxmail.com] + */ +@SuppressWarnings("AlibabaAbstractClassShouldStartWithAbstractNaming") +public abstract class ReflectUtil { + private ReflectUtil() { + } + + public static void foreachField(Class clazz) { + + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java b/asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java new file mode 100644 index 0000000..d59e99c --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java @@ -0,0 +1,41 @@ +package schedulingtest; + +import java.io.*; +import java.util.Objects; + +/** + * @author create by TcSnZh on 2021/5/17-上午11:34 + */ +public class FileStringReader { + public static String readFile(String resourceClasspath) { + InputStream is = null; + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( + is = Objects.requireNonNull( + Thread.currentThread().getContextClassLoader(), "get classLoader is null" + ).getResourceAsStream(resourceClasspath), () -> "get resource " + resourceClasspath + " is null" + ))); + StringWriter stringWriter = new StringWriter(); + char[] buf = new char[4096]; + int len; + while ((len = reader.read(buf)) > 0) { + stringWriter.write(buf, 0, len); + } + return stringWriter.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void main(String[] args) { + System.out.println("test readFile : \n\n" + readFile("case1_1.json")); + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java b/asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java new file mode 100644 index 0000000..758ce5f --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java @@ -0,0 +1,17 @@ +package schedulingtest; + +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.wrapper.WorkerWrapper; + +import java.util.Map; + +/** + * @author create by TcSnZh on 2021/5/17-上午2:31 + */ +public class PrintParam implements IWorker { + @Override + public Object action(Object object, Map> allWrappers) { + System.out.println("print param : " + object); + return object; + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java b/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java new file mode 100644 index 0000000..81a04f1 --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java @@ -0,0 +1,14 @@ +package schedulingtest.cases; + +import com.jd.platform.async.scheduling.SchedulingJsonParser; +import schedulingtest.FileStringReader; + +/** + * @author create by TcSnZh on 2021/5/17-上午11:49 + */ +class Case1 { + public static void main(String[] args) { + String json = FileStringReader.readFile("test.json"); + System.out.println(SchedulingJsonParser.getDefaultInstance().parse(json)); + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java b/asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java new file mode 100644 index 0000000..9b5a4de --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java @@ -0,0 +1,52 @@ +package schedulingtest.entity; + +/** + * @author create by TcSnZh on 2021/5/17-上午1:33 + */ +public class User { + private String name; + private int age; + private double money; + + public User() { + } + + public User(String name, int age, double money) { + this.name = name; + this.age = age; + this.money = money; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public double getMoney() { + return money; + } + + public void setMoney(double money) { + this.money = money; + } + + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + ", age=" + age + + ", money=" + money + + '}'; + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java b/asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java new file mode 100644 index 0000000..9bc5d34 --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java @@ -0,0 +1,37 @@ +package schedulingtest.impl; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; +import schedulingtest.entity.User; + +import java.util.Map; + +import static schedulingtest.impl.SelectUserByName.HundredMillion; + +/** + * @author create by TcSnZh on 2021/5/17-上午2:04 + */ +public class PayTaxes implements IWorker, ICallback { + /** + * 富人(资产>1亿)收30%的税,穷人发1000块。 + */ + @Override + public User action(String selectUserWrapperId, Map> allWrappers) { + User u = (User) allWrappers.get(selectUserWrapperId).getWorkResult().getResult(); + double money; + if ((money = u.getMoney()) > 1.00 * HundredMillion) { + u.setMoney(money * 0.7); + } else { + u.setMoney(money + 1000); + } + return u; + } + + @Override + public void result(boolean success, String ignore, WorkResult workResult) { + User u = workResult.getResult(); + (success ? System.out : System.err).printf("User %s has %.2f yuan .\n", u.getName(), u.getMoney()); + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java b/asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java new file mode 100644 index 0000000..a48f2af --- /dev/null +++ b/asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java @@ -0,0 +1,50 @@ +package schedulingtest.impl; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; +import schedulingtest.entity.User; + +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author create by TcSnZh on 2021/5/17-上午1:33 + */ +public class SelectUserByName implements IWorker, ICallback { + public static final long HundredMillion = 100000000; + private static final Map datasource; + + static { + datasource = new ConcurrentHashMap<>(); + User pony = new User("Pony", 50, 612.0 * HundredMillion); + User jackMa = new User("JackMa", 57, 475.0 * HundredMillion); + User me = new User("tcsnzh", 20, 0.01); + datasource.put(pony.getName(), pony); + datasource.put(jackMa.getName(), jackMa); + datasource.put(me.getName(), me); + } + + private static final AtomicInteger queryCount = new AtomicInteger(); + + /** + * 查询用户 + */ + @Override + public User action(String param, Map> allWrappers) { + return datasource.get(param); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + (success ? System.out : System.err).println( + "query[" + queryCount.getAndIncrement() + "] " + + "with parameter " + param + + (success ? " succeeded" : " failed") + + ". workResult is " + workResult); + } +} diff --git a/asyncTool-scheduling/src/test/resources/case1_1.json b/asyncTool-scheduling/src/test/resources/case1_1.json new file mode 100644 index 0000000..43bf794 --- /dev/null +++ b/asyncTool-scheduling/src/test/resources/case1_1.json @@ -0,0 +1,61 @@ +{ + "drawingsName": "case1_1", + "wrappers": [ + { + "id": "first", + "param": "JackMa", + "worker": "schedulingtest.impl.SelectUserByName", + "callback": { + "className:": "schedulingtest.impl.SelectUserByName" + }, + "wrapperStrategy": { + "dependWrapperStrategyMapper": null, + "dependMustStrategyMapper": null, + "dependenceStrategy": "ALL_DEPENDENCIES_ALL_SUCCESS", + "skipStrategy": "CHECK_ONE_LEVEL" + }, + "allowInterrupt": true, + "enableTimeout": true, + "timeoutLength": 50, + "timeoutUnit": "TimeUnit.MILLISECONDS" + }, + { + "id": "second", + "workerAndCallback": "schedulingtest.impl.PayTaxes" + }, + { + "id": "third", + "__extend": "second" + }, + { + "id": "fourth", + "worker": "schedulingtest.impl.PrintParam", + "wrapperStrategy": { + "dependenceStrategy": "ALL_DEPENDENCIES_ALL_SUCCESS" + } + } + ], + "relations": [ + { + "from": "first", + "to": "second" + }, + { + "from": "second", + "to": "fourth" + }, + { + "from": "third", + "to": "fourth" + } + ], + "beginWork": { + "timeoutLength": 100, + "timeoutUnit": "TimeUnit.MILLISECONDS", + "wrappers": [ + "first" + ], + "executor": "COMMON_POOL" + }, + "objects": [] +} \ No newline at end of file diff --git a/asyncTool-scheduling/src/test/resources/test.json b/asyncTool-scheduling/src/test/resources/test.json new file mode 100644 index 0000000..09df068 --- /dev/null +++ b/asyncTool-scheduling/src/test/resources/test.json @@ -0,0 +1,9 @@ +{ + "drawingsName": "case1_1", + "wrappers": [ + { + "id": "first", + "worker": "schedulingtest.impl.SelectUserByName" + } + ] +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6459b99..7d4ab74 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,8 @@ com.jd.platform asyncTool - 1.4.1-SNAPSHOT + pom + 1.5.0-SNAPSHOT @@ -21,4 +22,9 @@ + + asyncTool-openutil + asyncTool-core + asyncTool-scheduling + \ No newline at end of file -- Gitee From 0ada03e4139572868eeefbe7f2d6771b295f4b7c Mon Sep 17 00:00:00 2001 From: TcSnZh <1293969878@qq.com> Date: Wed, 19 May 2021 22:56:07 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20src/?= =?UTF-8?q?test/java/beforev14/depend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/beforev14/depend/LambdaTest.java | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 src/test/java/beforev14/depend/LambdaTest.java diff --git a/src/test/java/beforev14/depend/LambdaTest.java b/src/test/java/beforev14/depend/LambdaTest.java deleted file mode 100644 index 68a8386..0000000 --- a/src/test/java/beforev14/depend/LambdaTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package beforev14.depend; - -import java.util.Map; - -import com.jd.platform.async.executor.Async; -import com.jd.platform.async.worker.WorkResult; -import com.jd.platform.async.wrapper.WorkerWrapper; - -/** - * @author sjsdfg - * @since 2020/6/14 - */ -class LambdaTest { - public static void main(String[] args) throws Exception { - WorkerWrapper, String> workerWrapper2 = new WorkerWrapper.Builder, String>() - .worker((WorkResult result, Map> allWrappers) -> { - System.out.println("par2的入参来自于par1: " + result.getResult()); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return result.getResult().getName(); - }) - .callback((boolean success, WorkResult param, WorkResult workResult) -> - System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) - .id("third") - .build(); - - WorkerWrapper, User> workerWrapper1 = new WorkerWrapper.Builder, User>() - .worker((WorkResult result, Map> allWrappers) -> { - System.out.println("par1的入参来自于par0: " + result.getResult()); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return new User("user1"); - }) - .callback((boolean success, WorkResult param, WorkResult workResult) -> - System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) - .id("second") - .next(workerWrapper2) - .build(); - - WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker((String object, Map> allWrappers) -> { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return new User("user0"); - }) - .param("0") - .id("first") - .next(workerWrapper1, true) - .callback((boolean success, String param, WorkResult workResult) -> - System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) - .build(); - - //虽然尚未执行,但是也可以先取得结果的引用,作为下一个任务的入参。V1.2前写法,需要手工给 - //V1.3后,不用给wrapper setParam了,直接在worker的action里自行根据id获取即可.参考dependnew包下代码 - WorkResult result = workerWrapper.getWorkResult(); - WorkResult result1 = workerWrapper1.getWorkResult(); - workerWrapper1.setParam(result); - workerWrapper2.setParam(result1); - - Async.beginWork(3500, workerWrapper); - - System.out.println(workerWrapper2.getWorkResult()); - Async.shutDown(); - } -} -- Gitee From fd6dc77ffd55cd1997d42f4463ac42d0265462d6 Mon Sep 17 00:00:00 2001 From: TcSnZh Date: Thu, 20 May 2021 21:26:09 +0800 Subject: [PATCH 3/5] =?UTF-8?q?v1.5.3=20=E4=BF=AE=E6=94=B9=E4=BA=86?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=B0=91=E6=95=B0=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E4=BD=86=E6=98=AF=E8=BF=98=E6=B2=A1=E5=86=99=E5=AE=8C=EF=BC=8C?= =?UTF-8?q?=E6=9C=89=E4=BA=BA=E8=A6=81=E4=B8=80=E8=B5=B7=E5=86=99=EF=BC=8C?= =?UTF-8?q?=E6=89=80=E4=BB=A5push=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QuickStart.md | 66 +++--- .../jd/platform/async/callback/ICallback.java | 2 +- .../java/beforev14/depend/LambdaTest.java | 75 ++++++ .../src/test/java/v15/cases/Case6.java | 3 + .../async/scheduling/AsyncScheduling.java | 9 + .../DefaultSchedulingJsonParser.java | 40 ---- .../DefaultSchedulingDrawingsParser.java | 219 ++++++++++++++++++ .../drawings/SchedulingDrawings.java | 10 + .../drawings/SchedulingDrawingsImpl.java | 16 ++ .../drawings/SchedulingDrawingsParser.java | 18 ++ ...n.java => IllegalSchedulingException.java} | 12 +- .../IllegalSchedulingPropertyException.java | 55 +++++ .../AbstractSchedulingFactory.java} | 13 +- .../DefaultSchedulingJsonModelParser.java | 33 +++ .../async/scheduling/model/ObjectModel.java | 15 +- .../model/SchedulingDrawingsModel.java | 45 ++++ .../SchedulingJsonModelParser.java} | 15 +- .../async/scheduling/util/ReflectUtil.java | 3 - .../test/java/schedulingtest/cases/Case1.java | 14 -- .../schedulingtest/FileStringReader.java | 6 +- .../v15/schedulingtest/cases/case1/Case1.java | 15 ++ .../schedulingtest/cases/case1}/PayTaxes.java | 9 +- .../cases/case1}/PrintParam.java | 4 +- .../cases/case1}/SelectUserByName.java | 8 +- .../schedulingtest/cases/case1}/User.java | 4 +- .../src/test/resources/test.json | 9 - .../src/test/resources/{ => v15}/case1_1.json | 45 ++-- 27 files changed, 610 insertions(+), 153 deletions(-) create mode 100644 asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java rename asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/{IllegalConfigException.java => IllegalSchedulingException.java} (43%) create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java rename asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/{SchedulingFactory.java => factory/AbstractSchedulingFactory.java} (71%) create mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java rename asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/{SchedulingJsonParser.java => model/SchedulingJsonModelParser.java} (30%) delete mode 100644 asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java rename asyncTool-scheduling/src/test/java/{ => v15}/schedulingtest/FileStringReader.java (88%) create mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java rename asyncTool-scheduling/src/test/java/{schedulingtest/impl => v15/schedulingtest/cases/case1}/PayTaxes.java (77%) rename asyncTool-scheduling/src/test/java/{schedulingtest => v15/schedulingtest/cases/case1}/PrintParam.java (79%) rename asyncTool-scheduling/src/test/java/{schedulingtest/impl => v15/schedulingtest/cases/case1}/SelectUserByName.java (88%) rename asyncTool-scheduling/src/test/java/{schedulingtest/entity => v15/schedulingtest/cases/case1}/User.java (94%) delete mode 100644 asyncTool-scheduling/src/test/resources/test.json rename asyncTool-scheduling/src/test/resources/{ => v15}/case1_1.json (49%) diff --git a/QuickStart.md b/QuickStart.md index a475d5a..690e952 100644 --- a/QuickStart.md +++ b/QuickStart.md @@ -1048,33 +1048,34 @@ SetNext specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerW class Case6 { private static WorkerWrapperBuilder builder(String id) { return WorkerWrapper.builder() - .id(id) - .worker((param, allWrappers) -> { - System.out.println("wrapper(id=" + id + ") is working"); - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return null; - }); + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); } public static void main(String[] args) throws ExecutionException, InterruptedException { WorkerWrapper b = builder("B") - // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 - .depends((dependWrappers, thisWrapper, fromWrapper) -> - DependenceAction.FAST_FAIL - .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) - ) - .build(); + // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 + .depends((dependWrappers, thisWrapper, fromWrapper) -> + DependenceAction.FAST_FAIL + .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) + ) + .callback(ICallback.PRINT_EXCEPTION_STACK_TRACE) + .build(); WorkerWrapper a = builder("A") - .setNext() - // a将会使b直接开始工作 - // 若是去掉这行代码,则b会失败 - .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) - .wrapper(b) - .end().build(); + .setNext() + // a将会使b直接开始工作 + // 若是去掉这行代码,则b会失败 + .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) + .wrapper(b) + .end().build(); Async.beginWork(1000, a); System.out.println(a.getWorkResult()); System.out.println(b.getWorkResult()); @@ -1394,9 +1395,9 @@ public interface WorkerWrapperBuilder { >```xml > > -> asyncTool-scheduling -> com.jd.platform -> 1.5.0-SNAPSHOT +> asyncTool-scheduling +> com.jd.platform +> 1.5.0-SNAPSHOT > >``` @@ -1439,14 +1440,14 @@ wrappers:[ // 而且:不能与wrappers数组中其他的对象的id属性相同。即必须保证id唯一。 // 不允许undefined,不允许null。 "id": "first", - // param,即参数。请看《param属性格式》 + // param,即参数。请看《param》 // useObjectModel代表value的值是否是“对象模型”。 - // 允许undefined,如果undefined则使用null。显然,允许null。 + // 允许undefined或null,视为{"useObjectModel": false,"value": null} "param": { "useObjectModel": false, "value": "JackMa" }, - // 传入对象模型,请看《对象模型》 + // 传入对象模型,请看《对象模型ObjectModel》 // 不允许undefined或null "worker": { "sameObjectId": 1, @@ -1457,8 +1458,9 @@ wrappers:[ "callback": { "sameObjectId": 1 }, - // wrapper依赖策略 + // wrapper策略 // 允许undefined与null。如果为两者则使用com.jd.platform.async.wrapper.strategy.WrapperStrategy.DefaultWrapperStrategy + // 即"ALL_DEPENDENCIES_ALL_SUCCESS"与"CHECK_ONE_LEVEL" "wrapperStrategy": { // 传入{}键值对,键名为即wrapper的id属性,值为对象模型。 // 允许undefined和null,两者之意与空键值对{}并无二致 @@ -1522,7 +1524,7 @@ wrappers:[ `useObjectModel`属性用于说明`value`属性的所代表的对象类型: * 为false:使用json所对应的类型。 -* 为true:使用《实现类对象规范》中的我们自定义的对象模型。 +* 为true:使用《对象模型`ObjectModel`》中的我们自定义的对象模型。 ```json { @@ -1604,8 +1606,12 @@ beginWork: { // 如果设置了该属性,其他的属性均会被忽视。(优先级低于constObjectName) "sameObjectId": 1, // 提供类的全限定名字符串,将调用无参构造方法进行初始化 + // 如果constObjectName设置了非null且非undefined值,则此值允许为null或undefined + // 如果sameObjectId设置了非null且非undefined值,则id相同的对象模型中允许且只允许一个值为非null或非undefined,其他的都必须为null或undefined "className": "your.package.name.YourKlassName", // 初始化后,会根据该值来修改对象属性 + // 允许为null或undefined,表示不额外设置属性 + "properties": { // 其中的键值对为各字段名。这些字段需要有getter、setter方法。 "myIntegerField": 123123 diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java index 1087366..fe0b505 100755 --- a/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java @@ -29,7 +29,7 @@ public interface ICallback { /** * 提供常量选项:打印异常信息,跳过时的异常{@link SkippedException}不会打印。 */ - ICallback PRINT_EXCEPTION_STACK_TRACE = new ICallback() { + ICallback PRINT_EXCEPTION_STACK_TRACE = new ICallback() { @Override public void result(boolean success, Object param, WorkResult workResult) { Exception ex = workResult.getEx(); diff --git a/asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java b/asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java new file mode 100644 index 0000000..7dbaeca --- /dev/null +++ b/asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java @@ -0,0 +1,75 @@ +package beforev14.depend; + +import java.util.Map; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; + +/** + * @author sjsdfg + * @since 2020/6/14 + */ +@SuppressWarnings({"deprecation", "RedundantStringFormatCall"}) +class LambdaTest { + public static void main(String[] args) throws Exception { + WorkerWrapper, String> workerWrapper2 = new WorkerWrapper.Builder, String>() + .worker((WorkResult result, Map> allWrappers) -> { + System.out.println("par2的入参来自于par1: " + result.getResult()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return result.getResult().getName(); + }) + .callback((boolean success, WorkResult param, WorkResult workResult) -> + System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) + .id("third") + .build(); + + WorkerWrapper, User> workerWrapper1 = new WorkerWrapper.Builder, User>() + .worker((WorkResult result, Map> allWrappers) -> { + System.out.println("par1的入参来自于par0: " + result.getResult()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return new User("user1"); + }) + .callback((boolean success, WorkResult param, WorkResult workResult) -> + System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) + .id("second") + .next(workerWrapper2) + .build(); + + WorkerWrapper workerWrapper = new WorkerWrapper.Builder() + .worker((String object, Map> allWrappers) -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return new User("user0"); + }) + .param("0") + .id("first") + .next(workerWrapper1, true) + .callback((boolean success, String param, WorkResult workResult) -> + System.out.println(String.format("thread is %s, param is %s, result is %s", Thread.currentThread().getName(), param, workResult))) + .build(); + + //虽然尚未执行,但是也可以先取得结果的引用,作为下一个任务的入参。V1.2前写法,需要手工给 + //V1.3后,不用给wrapper setParam了,直接在worker的action里自行根据id获取即可.参考dependnew包下代码 + WorkResult result = workerWrapper.getWorkResult(); + WorkResult result1 = workerWrapper1.getWorkResult(); + workerWrapper1.setParam(result); + workerWrapper2.setParam(result1); + + Async.beginWork(3500, workerWrapper); + + System.out.println(workerWrapper2.getWorkResult()); + Async.shutDown(); + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case6.java b/asyncTool-core/src/test/java/v15/cases/Case6.java index 552fecd..2368220 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case6.java +++ b/asyncTool-core/src/test/java/v15/cases/Case6.java @@ -1,5 +1,6 @@ package v15.cases; +import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.executor.Async; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.wrapper.WorkerWrapper; @@ -29,12 +30,14 @@ class Case6 { } public static void main(String[] args) throws ExecutionException, InterruptedException { + //noinspection unchecked WorkerWrapper b = builder("B") // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 .depends((dependWrappers, thisWrapper, fromWrapper) -> DependenceAction.FAST_FAIL .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) ) + .callback(ICallback.PRINT_EXCEPTION_STACK_TRACE) .build(); WorkerWrapper a = builder("A") .setNext() diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java new file mode 100644 index 0000000..413b609 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java @@ -0,0 +1,9 @@ +package com.jd.platform.async.scheduling; + +/** + * 入口方法类 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:05 + */ +public class AsyncScheduling { +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java deleted file mode 100644 index 47cbbc6..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/DefaultSchedulingJsonParser.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jd.platform.async.scheduling; - -import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; - -/** - * @author create by TcSnZh on 2021/5/17-上午1:22 - */ -public class DefaultSchedulingJsonParser implements SchedulingJsonParser { - - - // ========== singleton instance ========== - - /** - * 私有构造方法,需要通过{@link #getInstance()}方法获取单例。 - */ - private DefaultSchedulingJsonParser() { - } - - /** - * 获取单例 - */ - public static DefaultSchedulingJsonParser getInstance() { - return instance; - } - - private static final DefaultSchedulingJsonParser instance = new DefaultSchedulingJsonParser(); - - // ========== public methods ========== - - @Override - public SchedulingDrawingsModel parse(String json) { - - - - // todo - return null; - } - - // ========== util methods ========== -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java new file mode 100644 index 0000000..030804c --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java @@ -0,0 +1,219 @@ +package com.jd.platform.async.scheduling.drawings; + +import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; +import com.jd.platform.async.scheduling.model.ObjectModel; +import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:38 + */ +class DefaultSchedulingDrawingsParser implements SchedulingDrawingsParser { + static final DefaultSchedulingDrawingsParser instance = new DefaultSchedulingDrawingsParser(); + + @Override + public SchedulingDrawings parseDrawings(final SchedulingDrawingsModel model) throws IllegalSchedulingPropertyException { + final SchedulingDrawingsImpl drawings = new SchedulingDrawingsImpl(); + final String drawingsName = model.getDrawingsName(); + drawings.setName(drawingsName == null ? UUID.randomUUID().toString() : drawingsName); + final List wrappers = + propRequireNotNull(model.getWrappers(), "wrappers"); + // 缓存WrapperModelMap并检查id是否唯一 + final LinkedHashMap wrapperId2ModelMap = + new LinkedHashMap<>(wrappers.size()); + for (int i = 0; i < wrappers.size(); i++) { + final SchedulingDrawingsModel.WrapperModel wrapper = wrappers.get(i); + // 检查id是否重复,并存入map + final String id = propRequireNotNull(wrapper.getId(), "wrappers[" + i + "].id"); + if (null != wrapperId2ModelMap.put(id, wrapper)) { + throw new IllegalSchedulingPropertyException("Wrapper id \"" + id + "\" duplicate ."); + } + } + // 缓存ObjectModelMap并检查 + final Map wrappersObjectModelMap = wrappersObjectModelMap(wrappers, wrapperId2ModelMap.keySet()); + // 构造wrapperFactory + // todo 还没写完 + return null; + } + + // ========== private method ========== + + + // ========== util static methods ========== + + /** + * 缓存sameObjectId有有效值的ObjectModel,并检查: + * 1. ObjectModel参数是否符合格式规范 + * 2. wrapperStrategy.dependOnUpWrapperStrategyMapper 的键id是否存在于wrappers的id属性集合中 + * + * @param wrappers wrappers属性值的列表 + * @param wrappersIdSet 已经包括wrappers列表中所有id属性的Set + * @return 返回Map的键为ObjectModel的sameObjectId,值为ObjectModel + * @throws IllegalSchedulingPropertyException 格式错误,抛出异常 + */ + protected static Map wrappersObjectModelMap( + List wrappers, + Set wrappersIdSet + ) throws IllegalSchedulingPropertyException { + final LinkedHashMap objectModelMap = new LinkedHashMap<>(); + for (final SchedulingDrawingsModel.WrapperModel wrapper : wrappers) { + final String prefixPropName = "wrappers[id=" + wrapper.getId() + "]"; + final SchedulingDrawingsModel.WrapperModel.ParamModel param = wrapper.getParam(); + // 将param参数中的objectModel检查并存入map + if (param != null && Boolean.TRUE.equals( + propRequireNotNull(param.getUseObjectModel(), prefixPropName + ".param.useObjectModel") + )) { + final Object value = param.getValue(); + if (value instanceof ObjectModel) { + checkAndPutObjectModelHasDifferentId( + (ObjectModel) value, + objectModelMap, + prefixPropName + ".param.value"); + } else { + throw IllegalSchedulingPropertyException.illegalFieldParameter( + prefixPropName + ".param.value", + value, + "it should instanceof ObjectModel." + ); + } + } + // 检查并缓存worker的objectModel + final String _workerPropName = prefixPropName + ".worker"; + checkAndPutObjectModelHasDifferentId(propRequireNotNull(wrapper.getWorker(), _workerPropName), + objectModelMap, _workerPropName); + // 检查并缓存callback的objectModel + checkAndPutObjectModelHasDifferentId(wrapper.getCallback(), objectModelMap, prefixPropName + ".callback"); + // 检查wrapperStrategy + final SchedulingDrawingsModel.WrapperModel.WrapperStrategyModel wrapperStrategy = wrapper.getWrapperStrategy(); + if (wrapperStrategy != null) { + // 检查并缓存dependOnUpWrapperStrategyMapper + final Map dependOnUpWrapperStrategyMapper = + wrapperStrategy.getDependOnUpWrapperStrategyMapper(); + if (dependOnUpWrapperStrategyMapper != null) { + for (Map.Entry entry : + dependOnUpWrapperStrategyMapper.entrySet()) { + final String wrapperId = entry.getKey(); + final String mapperPropName = prefixPropName + ".wrapperStrategy.dependOnUpWrapperStrategyMapper"; + if (!wrappersIdSet.contains(wrapperId)) { + throw IllegalSchedulingPropertyException.illegalFieldParameter( + mapperPropName, + dependOnUpWrapperStrategyMapper, + "the key(wrapperId) of \"" + wrapperId + "\n not in wrappers list" + ); + } + checkAndPutObjectModelHasDifferentId( + propRequireNotNull(entry.getValue(), mapperPropName), + objectModelMap, + mapperPropName + ); + } + } + // 检查并缓存dependenceStrategy + final ObjectModel dependenceStrategy = wrapperStrategy.getDependenceStrategy(); + final String dependenceStrategyPropName = prefixPropName + ".wrapperStrategy.dependenceStrategy"; + if (dependenceStrategy != null) { + checkAndPutObjectModelHasDifferentId( + dependenceStrategy, + objectModelMap, + dependenceStrategyPropName + ); + } + // 检查并缓存skipStrategy + final String skipStrategyPropName = prefixPropName + ".wrapperStrategy.skipStrategy"; + final ObjectModel skipStrategy = wrapperStrategy.getSkipStrategy(); + if (skipStrategy != null) { + checkAndPutObjectModelHasDifferentId( + skipStrategy, + objectModelMap, + skipStrategyPropName); + } + } + } + return objectModelMap; + } + + /** + * 本方法为{@link #wrappersObjectModelMap(List, Set)}的子方法。 + * 用于抽取ObjectModel格式的判断逻辑。 + * + * @param objectModel 对象模型 + * @param objectModelMap 要存入的Map + * @param propNameSup 属性名 + * @throws IllegalSchedulingPropertyException 格式错误,抛出异常 + */ + private static void checkAndPutObjectModelHasDifferentId(ObjectModel objectModel, + Map objectModelMap, + Supplier propNameSup) + throws IllegalSchedulingPropertyException { + final String constObjectName = objectModel.getConstObjectName(); + if (constObjectName != null) { + if (!ObjectModel.containConstObject(constObjectName)) { + final String propName; + throw new IllegalSchedulingPropertyException( + (propNameSup == null || (propName = propNameSup.get()) == null ? "" : "Property " + propName + " ") + + "ObjectModel doesn't have a constant object named \"" + + constObjectName + "\" . objectModel is " + objectModel); + } + return; + } + final Long sameObjectId = objectModel.getSameObjectId(); + if (sameObjectId != null) { + final Map properties = objectModel.getProperties(); + if (objectModelMap.containsKey(sameObjectId) && + // 同一id的对象,其他属性不允许重复设置 + (objectModel.getClassName() != null || properties != null && !properties.isEmpty())) { + throw new IllegalSchedulingPropertyException( + "The objectModel which sameObjectId=" + sameObjectId + + " cannot be set \"className\" or \"properties\" again . the two in conflict is " + + objectModel + " and " + objectModelMap.get(sameObjectId) + " ." + ); + } + objectModelMap.put(sameObjectId, objectModel); + return; + } + propRequireNotNull(objectModel.getClassName(), propNameSup.get()); + + } + + private static void checkAndPutObjectModelHasDifferentId(ObjectModel objectModel, + Map objectModelMap, + String propName) + throws IllegalSchedulingPropertyException { + checkAndPutObjectModelHasDifferentId(objectModel, objectModelMap, () -> propName); + } + + /** + * 检查属性是否为null + * + * @param prop 属性值 + * @param propName 属性名 + * @param reason 原因说明。 + * 默认值为{@code "it's not allow null"},用于{@link #propRequireNotNull(Object, String)}与 + * {@link #propRequireNotNull(Object, Supplier)}方法中。 + * @param 属性值泛型 + * @return 返回传入的属性值,供继续调用 + * @throws IllegalSchedulingPropertyException 如果属性为null,抛出异常 + */ + private static T propRequireNotNull(T prop, Supplier propName, Supplier reason) + throws IllegalSchedulingPropertyException { + if (prop == null) { + throw IllegalSchedulingPropertyException.illegalFieldParameter( + propName.get(), + null, + reason.get() + ); + } + return prop; + } + + private static T propRequireNotNull(T prop, String propName) throws IllegalSchedulingPropertyException { + return propRequireNotNull(prop, () -> propName); + } + + private static T propRequireNotNull(T prop, Supplier propName) throws IllegalSchedulingPropertyException { + return propRequireNotNull(prop, propName, () -> "it's not allow null"); + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java new file mode 100644 index 0000000..f0b5b4b --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java @@ -0,0 +1,10 @@ +package com.jd.platform.async.scheduling.drawings; + +/** + * 工厂图纸,可从模型转化而来 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:00 + */ +public interface SchedulingDrawings { + +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java new file mode 100644 index 0000000..7cf3a4b --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java @@ -0,0 +1,16 @@ +package com.jd.platform.async.scheduling.drawings; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:39 + */ +class SchedulingDrawingsImpl implements SchedulingDrawings{ + protected String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java new file mode 100644 index 0000000..178f47b --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java @@ -0,0 +1,18 @@ +package com.jd.platform.async.scheduling.drawings; + +import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; +import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:03 + */ +public interface SchedulingDrawingsParser { + /** + * 从模型转化为图纸 + */ + SchedulingDrawings parseDrawings(SchedulingDrawingsModel model) throws IllegalSchedulingPropertyException; + + default SchedulingDrawingsParser getDefaultInstance() { + return DefaultSchedulingDrawingsParser.instance; + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java similarity index 43% rename from asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java rename to asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java index 21b55e6..8a49582 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalConfigException.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java @@ -3,23 +3,23 @@ package com.jd.platform.async.scheduling.exception; /** * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:20 */ -public class IllegalConfigException extends Exception{ - public IllegalConfigException() { +public class IllegalSchedulingException extends Exception{ + public IllegalSchedulingException() { } - public IllegalConfigException(String message) { + public IllegalSchedulingException(String message) { super(message); } - public IllegalConfigException(String message, Throwable cause) { + public IllegalSchedulingException(String message, Throwable cause) { super(message, cause); } - public IllegalConfigException(Throwable cause) { + public IllegalSchedulingException(Throwable cause) { super(cause); } - public IllegalConfigException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + public IllegalSchedulingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java new file mode 100644 index 0000000..459c65a --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java @@ -0,0 +1,55 @@ +package com.jd.platform.async.scheduling.exception; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:59 + */ +public class IllegalSchedulingPropertyException extends IllegalSchedulingException { + + public IllegalSchedulingPropertyException(String message) { + super(message); + } + + public IllegalSchedulingPropertyException(String message, Throwable cause) { + super(message, cause); + } + + /** + * json反序列化失败 + * + * @param json json + * @param cause 原因 + * @return 返回异常 + */ + public static IllegalSchedulingPropertyException deserializeJsonFailed(String json, + Throwable cause) { + return new IllegalSchedulingPropertyException( + "Json deserialize to model failed , please check properties and see QuickStart.md . the json is : " + json, + cause + ); + } + + /** + * 如果json反序列化有效,但是不符合规范。 + * + * @param illegalFieldName 无效的属性名 + * @param fieldValue 属性的值 + * @param reason 原因 + * @param cause 引发异常,可以为null + * @return 返回异常 + */ + public static IllegalSchedulingPropertyException illegalFieldParameter(String illegalFieldName, + Object fieldValue, + String reason, + Throwable cause) { + return new IllegalSchedulingPropertyException( + "Property" + illegalFieldName + " does not conform to specification. value is : " + fieldValue + + " . because " + reason, cause + ); + } + + public static IllegalSchedulingPropertyException illegalFieldParameter(String illegalFieldName, + Object fieldValue, + String reason) { + return illegalFieldParameter(illegalFieldName, fieldValue, reason, null); + } +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java similarity index 71% rename from asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java rename to asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java index 49c7c7d..516b23a 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingFactory.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java @@ -1,22 +1,19 @@ -package com.jd.platform.async.scheduling; - -import sun.misc.Unsafe; +package com.jd.platform.async.scheduling.factory; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; /** * 调度工厂。传入图纸生成一组wrapper。 * * @author create by TcSnZh on 2021/5/17-上午1:11 */ -public class SchedulingFactory { - private final String factoryName; +public abstract class AbstractSchedulingFactory { + protected final String factoryName; /** * 无参构造,默认使用 {@code 栈信息<自增long值> } 作为工厂名 */ - public SchedulingFactory() { + public AbstractSchedulingFactory() { this(Thread.currentThread().getStackTrace()[2] + "<" + defaultFactoryNameCount.getAndIncrement() + ">"); } @@ -25,7 +22,7 @@ public class SchedulingFactory { * * @param factoryName 工厂名 */ - public SchedulingFactory(String factoryName) { + public AbstractSchedulingFactory(String factoryName) { this.factoryName = factoryName; } diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java new file mode 100644 index 0000000..6e62218 --- /dev/null +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java @@ -0,0 +1,33 @@ +package com.jd.platform.async.scheduling.model; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; + +import static com.alibaba.fastjson.parser.Feature.*; + +/** + * @author create by TcSnZh on 2021/5/17-上午1:22 + */ +class DefaultSchedulingJsonModelParser implements SchedulingJsonModelParser { + // ========== singleton instance ========== + + static final DefaultSchedulingJsonModelParser instance = new DefaultSchedulingJsonModelParser(); + + // ========== public methods ========== + + @Override + public SchedulingDrawingsModel parseToModel(String json) throws IllegalSchedulingPropertyException { + try { + return JSONObject.parseObject(json, SchedulingDrawingsModel.class, DEFAULT_FEATURES); + } catch (Exception e) { + throw IllegalSchedulingPropertyException.deserializeJsonFailed(json, e); + } + } + + static Feature[] DEFAULT_FEATURES = { + AllowComment, AllowUnQuotedFieldNames, AllowSingleQuotes, SafeMode, ErrorOnEnumNotMatch + }; + + // ========== util methods ========== +} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java index 6171899..ea4783c 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java @@ -11,7 +11,6 @@ import java.util.Map; /** * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:51 */ -@SuppressWarnings("unused") public class ObjectModel { protected String constObjectName; protected String className; @@ -57,6 +56,16 @@ public class ObjectModel { return constObjects; } + @Override + public String toString() { + return "ObjectModel{" + + "constObjectName='" + constObjectName + '\'' + + ", className='" + className + '\'' + + ", sameObjectId=" + sameObjectId + + ", properties=" + properties + + '}'; + } + // static constants private static final Map constObjects; @@ -73,6 +82,10 @@ public class ObjectModel { constObjects.put("PRINT_EXCEPTION_STACK_TRACE", ICallback.PRINT_EXCEPTION_STACK_TRACE); } + public static boolean containConstObject(String name) { + return constObjects.containsKey(name); + } + public static T getConstObject(String name) { //noinspection unchecked return (T) constObjects.get(name); diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java index 1e00e68..6899622 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java @@ -2,6 +2,7 @@ package com.jd.platform.async.scheduling.model; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.concurrent.TimeUnit; /** @@ -159,6 +160,22 @@ public class SchedulingDrawingsModel { public void setExtendConfig(String extendConfig) { this.extendConfig = extendConfig; } + + @Override + public String toString() { + return "WrapperModel{" + + "id='" + id + '\'' + + ", param=" + param + + ", worker=" + worker + + ", callback=" + callback + + ", wrapperStrategy=" + wrapperStrategy + + ", allowInterrupt=" + allowInterrupt + + ", enableTimeout=" + enableTimeout + + ", timeoutLength=" + timeoutLength + + ", timeUnit=" + timeUnit + + ", extendConfig='" + extendConfig + '\'' + + '}'; + } } public static class RelationModel { @@ -180,6 +197,14 @@ public class SchedulingDrawingsModel { public void setTo(Object to) { this.to = to; } + + @Override + public String toString() { + return "RelationModel{" + + "from=" + from + + ", to=" + to + + '}'; + } } public static class BeginWorkModel { @@ -219,6 +244,16 @@ public class SchedulingDrawingsModel { public void setExecutor(String executor) { this.executor = executor; } + + @Override + public String toString() { + return "BeginWorkModel{" + + "timeoutLength=" + timeoutLength + + ", timeoutUnit=" + timeoutUnit + + ", wrappers=" + wrappers + + ", executor='" + executor + '\'' + + '}'; + } } public String getDrawingsName() { @@ -252,4 +287,14 @@ public class SchedulingDrawingsModel { public void setBeginWork(BeginWorkModel beginWork) { this.beginWork = beginWork; } + + @Override + public String toString() { + return "SchedulingDrawingsModel{" + + "drawingsName='" + drawingsName + '\'' + + ", wrappers=" + wrappers + + ", relations=" + relations + + ", beginWork=" + beginWork + + '}'; + } } diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java similarity index 30% rename from asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java rename to asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java index ee4b908..f3cf69a 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/SchedulingJsonParser.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java @@ -1,24 +1,23 @@ -package com.jd.platform.async.scheduling; +package com.jd.platform.async.scheduling.model; -import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; +import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; /** * @author create by TcSnZh on 2021/5/17-下午7:22 */ -public interface SchedulingJsonParser { - +public interface SchedulingJsonModelParser { /** - * 解析json为图纸对象 + * 解析json为配置模型对象 * * @param json json * @return 返回图纸对象接口 */ - SchedulingDrawingsModel parse(String json); + SchedulingDrawingsModel parseToModel(String json) throws IllegalSchedulingPropertyException; /** * 默认实现 */ - static SchedulingJsonParser getDefaultInstance() { - return DefaultSchedulingJsonParser.getInstance(); + static SchedulingJsonModelParser getDefaultInstance() { + return DefaultSchedulingJsonModelParser.instance; } } diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java index cdd7b97..62d1675 100644 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java +++ b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java @@ -10,7 +10,4 @@ public abstract class ReflectUtil { private ReflectUtil() { } - public static void foreachField(Class clazz) { - - } } diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java b/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java deleted file mode 100644 index 81a04f1..0000000 --- a/asyncTool-scheduling/src/test/java/schedulingtest/cases/Case1.java +++ /dev/null @@ -1,14 +0,0 @@ -package schedulingtest.cases; - -import com.jd.platform.async.scheduling.SchedulingJsonParser; -import schedulingtest.FileStringReader; - -/** - * @author create by TcSnZh on 2021/5/17-上午11:49 - */ -class Case1 { - public static void main(String[] args) { - String json = FileStringReader.readFile("test.json"); - System.out.println(SchedulingJsonParser.getDefaultInstance().parse(json)); - } -} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java similarity index 88% rename from asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java rename to asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java index d59e99c..bda04e1 100644 --- a/asyncTool-scheduling/src/test/java/schedulingtest/FileStringReader.java +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java @@ -1,4 +1,4 @@ -package schedulingtest; +package v15.schedulingtest; import java.io.*; import java.util.Objects; @@ -34,8 +34,4 @@ public class FileStringReader { } } } - - public static void main(String[] args) { - System.out.println("test readFile : \n\n" + readFile("case1_1.json")); - } } diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java new file mode 100644 index 0000000..8f40ae1 --- /dev/null +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java @@ -0,0 +1,15 @@ +package v15.schedulingtest.cases.case1; + +import com.jd.platform.async.scheduling.exception.IllegalSchedulingException; +import com.jd.platform.async.scheduling.model.SchedulingJsonModelParser; +import v15.schedulingtest.FileStringReader; + +/** + * @author create by TcSnZh on 2021/5/17-上午11:49 + */ +class Case1 { + public static void main(String[] args) throws IllegalSchedulingException { + String json = FileStringReader.readFile("v15/case1_1.json"); + System.out.println(SchedulingJsonModelParser.getDefaultInstance().parseToModel(json)); + } +} diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java similarity index 77% rename from asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java rename to asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java index 9bc5d34..71f6e56 100644 --- a/asyncTool-scheduling/src/test/java/schedulingtest/impl/PayTaxes.java +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java @@ -1,19 +1,16 @@ -package schedulingtest.impl; +package v15.schedulingtest.cases.case1; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.WorkerWrapper; -import schedulingtest.entity.User; import java.util.Map; -import static schedulingtest.impl.SelectUserByName.HundredMillion; - /** * @author create by TcSnZh on 2021/5/17-上午2:04 */ -public class PayTaxes implements IWorker, ICallback { +class PayTaxes implements IWorker, ICallback { /** * 富人(资产>1亿)收30%的税,穷人发1000块。 */ @@ -21,7 +18,7 @@ public class PayTaxes implements IWorker, ICallback public User action(String selectUserWrapperId, Map> allWrappers) { User u = (User) allWrappers.get(selectUserWrapperId).getWorkResult().getResult(); double money; - if ((money = u.getMoney()) > 1.00 * HundredMillion) { + if ((money = u.getMoney()) > 1.00 * SelectUserByName.HundredMillion) { u.setMoney(money * 0.7); } else { u.setMoney(money + 1000); diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java similarity index 79% rename from asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java rename to asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java index 758ce5f..c46166c 100644 --- a/asyncTool-scheduling/src/test/java/schedulingtest/PrintParam.java +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java @@ -1,4 +1,4 @@ -package schedulingtest; +package v15.schedulingtest.cases.case1; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.wrapper.WorkerWrapper; @@ -8,7 +8,7 @@ import java.util.Map; /** * @author create by TcSnZh on 2021/5/17-上午2:31 */ -public class PrintParam implements IWorker { +class PrintParam implements IWorker { @Override public Object action(Object object, Map> allWrappers) { System.out.println("print param : " + object); diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java similarity index 88% rename from asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java rename to asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java index a48f2af..b796933 100644 --- a/asyncTool-scheduling/src/test/java/schedulingtest/impl/SelectUserByName.java +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java @@ -1,13 +1,11 @@ -package schedulingtest.impl; +package v15.schedulingtest.cases.case1; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.WorkerWrapper; -import schedulingtest.entity.User; +import v15.schedulingtest.cases.case1.User; -import java.io.PrintStream; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -15,7 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * @author create by TcSnZh on 2021/5/17-上午1:33 */ -public class SelectUserByName implements IWorker, ICallback { +class SelectUserByName implements IWorker, ICallback { public static final long HundredMillion = 100000000; private static final Map datasource; diff --git a/asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java similarity index 94% rename from asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java rename to asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java index 9b5a4de..9a192ee 100644 --- a/asyncTool-scheduling/src/test/java/schedulingtest/entity/User.java +++ b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java @@ -1,9 +1,9 @@ -package schedulingtest.entity; +package v15.schedulingtest.cases.case1; /** * @author create by TcSnZh on 2021/5/17-上午1:33 */ -public class User { +class User { private String name; private int age; private double money; diff --git a/asyncTool-scheduling/src/test/resources/test.json b/asyncTool-scheduling/src/test/resources/test.json deleted file mode 100644 index 09df068..0000000 --- a/asyncTool-scheduling/src/test/resources/test.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "drawingsName": "case1_1", - "wrappers": [ - { - "id": "first", - "worker": "schedulingtest.impl.SelectUserByName" - } - ] -} \ No newline at end of file diff --git a/asyncTool-scheduling/src/test/resources/case1_1.json b/asyncTool-scheduling/src/test/resources/v15/case1_1.json similarity index 49% rename from asyncTool-scheduling/src/test/resources/case1_1.json rename to asyncTool-scheduling/src/test/resources/v15/case1_1.json index 43bf794..cedf0fb 100644 --- a/asyncTool-scheduling/src/test/resources/case1_1.json +++ b/asyncTool-scheduling/src/test/resources/v15/case1_1.json @@ -3,16 +3,25 @@ "wrappers": [ { "id": "first", - "param": "JackMa", - "worker": "schedulingtest.impl.SelectUserByName", - "callback": { + "param": { + "useObjectModel": false, + "value": "JackMa" + }, + "worker": { + "sameObjectId": 1, "className:": "schedulingtest.impl.SelectUserByName" }, + "callback": { + "sameObjectId": 1 + }, "wrapperStrategy": { - "dependWrapperStrategyMapper": null, - "dependMustStrategyMapper": null, - "dependenceStrategy": "ALL_DEPENDENCIES_ALL_SUCCESS", - "skipStrategy": "CHECK_ONE_LEVEL" + "dependOnUpWrapperStrategyMapper": null, + "dependenceStrategy": { + "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" + }, + "skipStrategy": { + "constObjectName": "CHECK_ONE_LEVEL" + } }, "allowInterrupt": true, "enableTimeout": true, @@ -21,7 +30,13 @@ }, { "id": "second", - "workerAndCallback": "schedulingtest.impl.PayTaxes" + "worker": { + "sameObjectId": 2, + "className:": "schedulingtest.impl.SelectUserByName" + }, + "callback": { + "sameObjectId": 2 + } }, { "id": "third", @@ -29,9 +44,14 @@ }, { "id": "fourth", - "worker": "schedulingtest.impl.PrintParam", + "worker": { + "sameObjectId": 2, + "className:": "schedulingtest.impl.PrintParam" + }, "wrapperStrategy": { - "dependenceStrategy": "ALL_DEPENDENCIES_ALL_SUCCESS" + "dependenceStrategy": { + "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" + } } } ], @@ -51,11 +71,10 @@ ], "beginWork": { "timeoutLength": 100, - "timeoutUnit": "TimeUnit.MILLISECONDS", + "timeoutUnit": "MILLISECONDS", "wrappers": [ "first" ], "executor": "COMMON_POOL" - }, - "objects": [] + } } \ No newline at end of file -- Gitee From a0c7e2893196cbd60e1322d2cda5dec351290ed2 Mon Sep 17 00:00:00 2001 From: TcSnZh Date: Wed, 26 May 2021 16:54:26 +0800 Subject: [PATCH 4/5] =?UTF-8?q?v1.5.1-SNAPSHOT=20=E4=BD=BF=E7=94=A8OnceWor?= =?UTF-8?q?k=E5=8F=A5=E6=9F=84=E6=9B=BF=E4=BB=A3=E4=BA=86=E6=97=A7?= =?UTF-8?q?=E7=9A=84boolean=E8=BF=94=E5=9B=9E=E5=80=BC=E3=80=82=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=8F=96=E6=B6=88=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QuickStart.md | 420 +++++------------- asyncTool-core/pom.xml | 4 +- .../exception/CancelSkippedException.java | 19 + .../async/exception/SkippedException.java | 15 +- .../com/jd/platform/async/executor/Async.java | 160 +++++-- .../async/executor/PollingCenter.java | 2 +- .../jd/platform/async/worker/OnceWork.java | 375 ++++++++++++++++ .../platform/async/wrapper/WorkerWrapper.java | 41 +- .../async/wrapper/WorkerWrapperGroup.java | 102 ++++- .../wrapper/strategy/WrapperStrategy.java | 4 +- .../src/test/java/v15/cases/Case0.java | 37 ++ .../src/test/java/v15/cases/Case01.java | 37 ++ .../src/test/java/v15/cases/Case02.java | 38 ++ .../src/test/java/v15/cases/Case1.java | 4 +- .../src/test/java/v15/cases/Case10.java | 72 +++ .../src/test/java/v15/cases/Case2.java | 2 +- .../src/test/java/v15/cases/Case3.java | 2 +- .../src/test/java/v15/cases/Case4.java | 2 +- .../src/test/java/v15/cases/Case5.java | 2 +- .../src/test/java/v15/cases/Case6.java | 2 +- .../src/test/java/v15/cases/Case7.java | 2 +- .../src/test/java/v15/cases/Case8.java | 2 +- .../src/test/java/v15/cases/Case9.java | 10 +- asyncTool-openutil/pom.xml | 2 +- .../com/jd/platform/async/openutil/BiInt.java | 2 +- .../async/openutil/collection/BiTree.java | 76 ---- .../collection/CollisionRangeTable.java | 10 - .../openutil/collection/DirectedGraph.java | 5 +- .../async/openutil/collection/Tree.java | 16 - .../openutil/collection/WheelIterator.java | 23 - .../async/openutil/concurrent/RangeLock.java | 46 -- .../async/openutil/unsafe/UnsafeUtil.java | 33 -- .../src/test/java/openutiltest/TestGraph.java | 2 +- asyncTool-scheduling/pom.xml | 31 -- .../async/scheduling/AsyncScheduling.java | 9 - .../DefaultSchedulingDrawingsParser.java | 219 --------- .../drawings/SchedulingDrawings.java | 10 - .../drawings/SchedulingDrawingsImpl.java | 16 - .../drawings/SchedulingDrawingsParser.java | 18 - .../exception/IllegalSchedulingException.java | 25 -- .../IllegalSchedulingPropertyException.java | 55 --- .../factory/AbstractSchedulingFactory.java | 32 -- .../DefaultSchedulingJsonModelParser.java | 33 -- .../async/scheduling/model/ObjectModel.java | 93 ---- .../model/SchedulingDrawingsModel.java | 300 ------------- .../model/SchedulingJsonModelParser.java | 23 - .../async/scheduling/util/ReflectUtil.java | 13 - .../v15/schedulingtest/FileStringReader.java | 37 -- .../v15/schedulingtest/cases/case1/Case1.java | 15 - .../schedulingtest/cases/case1/PayTaxes.java | 34 -- .../cases/case1/PrintParam.java | 17 - .../cases/case1/SelectUserByName.java | 48 -- .../v15/schedulingtest/cases/case1/User.java | 52 --- .../src/test/resources/v15/case1_1.json | 80 ---- pom.xml | 3 +- 55 files changed, 965 insertions(+), 1767 deletions(-) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case0.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case01.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case02.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case10.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java delete mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java delete mode 100644 asyncTool-scheduling/pom.xml delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java delete mode 100644 asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java delete mode 100644 asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java delete mode 100644 asyncTool-scheduling/src/test/resources/v15/case1_1.json diff --git a/QuickStart.md b/QuickStart.md index 690e952..ec1e1db 100644 --- a/QuickStart.md +++ b/QuickStart.md @@ -56,13 +56,7 @@ mvn install asyncTool-core com.jd.platform - 1.5.0-SNAPSHOT - - - - asyncTool-scheduling - com.jd.platform - 1.5.0-SNAPSHOT + 1.5.1-SNAPSHOT ``` @@ -77,7 +71,7 @@ mvn install > > com.jd.platform > asyncTool-core -> 1.5.0-SNAPSHOT +> 1.5.1-SNAPSHOT > > > ``` @@ -191,23 +185,6 @@ WorkerWrapper.builder() 以下为示例: ```java -package v15.cases; - -import com.jd.platform.async.callback.DefaultCallback; -import com.jd.platform.async.executor.Async; -import com.jd.platform.async.openutil.collection.CommonDirectedGraph; -import com.jd.platform.async.openutil.collection.DirectedGraph; -import com.jd.platform.async.wrapper.QuickBuildWorkerWrapper; -import com.jd.platform.async.wrapper.WorkerWrapper; -import com.jd.platform.async.wrapper.strategy.WrapperStrategy; - -import java.util.concurrent.*; - -/** - * 快速构造示例。 - * - * @author create by TcSnZh on 2021/5/17-下午5:23 - */ class Case9 { public static void main(String[] args) throws ExecutionException, InterruptedException { DirectedGraph, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>()); @@ -244,10 +221,16 @@ class Case9 { // System.out.println(graph); - Async.beginWork(200, w1); + Async.work(200, w1).awaitFinish(); System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); - + /* 输出: + I am IWorker 1 + I am IWorker 2 + Begin work end . + w1 : 省略 + w2 : 省略 + */ } } ``` @@ -259,7 +242,7 @@ class Case9 { ![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140256_8c015621_303698.png "屏幕截图.png") ```java -class Test { +class Case0 { static WorkerWrapperBuilder builder(String id) { return WorkerWrapper.builder() .id(id) @@ -274,15 +257,15 @@ class Test { WorkerWrapper b = builder("B").build(); WorkerWrapper c = builder("C").build(); try { - Async.beginWork(100, a, b, c); - } catch (ExecutionException | InterruptedException e) { + Async.work(100, a, b, c).awaitFinish(); + } catch (InterruptedException e) { e.printStackTrace(); } /* 输出: wrapper(id=A) is working wrapper(id=B) is working wrapper(id=C) is working - */ + */ } } ``` @@ -293,7 +276,7 @@ class Test { ![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140405_93800bc7_303698.png "屏幕截图.png") ```java -class Test { +class Case01 { static WorkerWrapperBuilder builder(String id) { return WorkerWrapper.builder() .id(id) @@ -309,8 +292,8 @@ class Test { WorkerWrapper c = builder("C").depends(a).build(); WorkerWrapper f = builder("F").depends(b, c).build(); try { - Async.beginWork(100, a); - } catch (ExecutionException | InterruptedException e) { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { e.printStackTrace(); } /* 输出: @@ -326,7 +309,7 @@ class Test { 如果觉得`.depneds()`方法的排序您不喜欢,也可以用`.nextOf()`这种方式: ```java -class Test { +class Case02 { static WorkerWrapperBuilder builder(String id) { return WorkerWrapper.builder() .id(id) @@ -343,8 +326,8 @@ class Test { .nextOf(builder("C").nextOf(f).build()) .build(); try { - Async.beginWork(100, a); - } catch (ExecutionException | InterruptedException e) { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { e.printStackTrace(); } /* 输出: @@ -396,8 +379,8 @@ class Case1 { ) .build(); try { - Async.beginWork(1000, a, d); - } catch (ExecutionException | InterruptedException e) { + Async.work(1000, a, d).awaitFinish(); + } catch (InterruptedException e) { e.printStackTrace(); } /* 输出: @@ -455,7 +438,7 @@ class Case2 { .id("id:200").worker(new AddWork()).param(200).build(); WorkerWrapper add = WorkerWrapper.builder().id("id:add") .worker(new AddWork("id:100", "id:200")).depends(wrapper100, wrapper200).build(); - Async.beginWork(20,wrapper100,wrapper200); + Async.work(20,wrapper100,wrapper200).awaitFinish(); System.out.println(add.getWorkResult()); // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} } @@ -469,11 +452,16 @@ class Case2 { `Async`工具类有多个方法可以使用自定义线程池 ```java -public static boolean beginWork(long timeout, +public static OnceWork work(long timeout, ExecutorService executorService, Collection> workerWrappers); -public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper); +public static OnceWork work(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper); + +public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers, + String workId); ``` 另外,如果没有指定线程池,默认会使用`COMMON_POOL`,您可以调用这些方法获得/关闭此线程池: @@ -496,7 +484,7 @@ public static synchronized boolean shutDownCommonPool(boolean now); 以下是一个使用自定义线程池的简单代码示例: ```java -Async.beginWork(1000, Executors.newFixedThreadPool(2),a); +Async.work(1000, Executors.newFixedThreadPool(2),a).awaitFinish(); ``` ## WorkerWrapper基本属性 @@ -548,6 +536,83 @@ V action(T object, Map> allWrappers); > 其他属性都写在源码注释中,可下载源码慢慢查看。 +## `OnceWork`任务句柄详解 + +`Async.work(...)`方法的返回值为`OnceWork`句柄。 + +> 源码里有大量的注释,直接看源码效率更高 + +#### 需要同步等待完成 + +`OnceWork`被返回后,任务并没有同步完成,而是还在运行。 + +如果我们需要同步完成,则调用`awaitFinish`方法即可同步等待。 + +#### 取消任务 + +调用`pleaseCancel`方法可取消任务,以下为示例: + +```java +class Case10 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })) + .allowInterrupt(true); + } + + /** + * A(10ms) ==> B(10ms) ==> C(10ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final WorkerWrapper c; + final WorkerWrapper b; + final WorkerWrapper a = builder("A", 10) + .nextOf(b = builder("B", 10) + .nextOf(c = builder("C", 10).build()) + .build()) + .build(); + final OnceWork onceWork = Async.work(40, a); + Thread.sleep(25); + onceWork.pleaseCancelAndAwaitFinish(); + System.out.println("任务b信息 " + b); + System.out.println("任务c信息 " + c); + System.out.println("OnceWork信息 " + onceWork); + /* + 可以看到C的state为SKIP,workResult.ex为CancelSkippedException,即被取消了。 + 不过有时程序运行慢,导致B被取消了,那么C就不会执行,其状态就为INIT了。 + */ + } +} +``` + ## 设置WorkerWrapper属性 ### 设置依赖策略 @@ -618,7 +683,7 @@ class Case3 { // 这里用线程数较少的线程池做示例,对于ALL_DEPENDENCIES_ANY_SUCCESS“仅需一个”的效果会好一点 ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, a); + Async.work(1000, pool, a).awaitFinish(); } finally { pool.shutdown(); } @@ -849,7 +914,7 @@ class Case4 { } ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, a); + Async.work(1000, pool, a).awaitFinish(); } finally { pool.shutdown(); } @@ -938,7 +1003,7 @@ class Case5 { WorkerWrapper start = builder("start").nextOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).build(); ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, start); + Async.work(1000, pool, start).awaitFinish(); } finally { pool.shutdown(); } @@ -1076,7 +1141,7 @@ class Case6 { .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) .wrapper(b) .end().build(); - Async.beginWork(1000, a); + Async.work(1000, a).awaitFinish(); System.out.println(a.getWorkResult()); System.out.println(b.getWorkResult()); /* 输出: @@ -1181,7 +1246,7 @@ class Case7 { .build(), builder("E", 5).nextOf(d).build() ).build(); - Async.beginWork(1000, a); + Async.work(1000, a).awaitFinish(); /* 输出: wrapper(id=A) is working wrapper(id=E) is working @@ -1302,7 +1367,7 @@ class Case8 { .nextOf(builder("C", 20).build()) .build()) .build(); - Async.beginWork(15, a); + Async.work(20, a).awaitFinish(); /* 输出: wrapper(id=A) has begin . wrapper(id=A) is working @@ -1347,7 +1412,6 @@ public interface WorkerWrapperBuilder { * 总任务超时,但本wrapper在WORKING。 * 单wrapper超时,但本wrapper在WORKING。 * wrapper应当被跳过,但本wrapper在WORKING。 - * 调用`WorkerWrapper#failNow()`方法,且wrapper在WORKING状态。 # 开放工具类 @@ -1361,14 +1425,14 @@ public interface WorkerWrapperBuilder { > > com.jd.platform > asyncTool-openutil -> 1.5.0-SNAPSHOT +> 1.5.1-SNAPSHOT > > > ``` ### 集合类 -> `com.jd.platform.async.openutil.collection.*` +> 普通集合包`com.jd.platform.async.openutil.collection.*` 这里不详述,要用的话源码里有注释。 @@ -1384,255 +1448,7 @@ public interface WorkerWrapperBuilder { ### 其他 -`com.jd.platform.async.openutil` - -* `BiInt` 一个表示两个int值的实体类,内含默认比较器、缓冲区间。 - -# 动态任务调度 - ->引入依赖 -> ->```xml -> -> -> asyncTool-scheduling -> com.jd.platform -> 1.5.0-SNAPSHOT -> ->``` - -动态任务调度,传个json就能让调度工厂随心所欲的构造wrapper与执行。 - -如果您已经熟悉**《任务编排》**这一章的内容,则可以试试这玩意。 - -## 基本组件 - -## 模型属性 - -### 字段属性详解 - -以下是一个传入的json的格式示例,注释中描述了属性含义与格式: - -```json -{ - // 图纸名称。可使用 字符串、null、不写。为null或不写,则会使用uuid - drawingsName :"", - // 提供的全部WorkerWrapper列表。 - // 内部格式请参考《wrappers》这章。 - wrappers:[], - // wrapper顺序关系。 - // 内部格式请参考《relations》这章 - relations: [], - // 任务启动参数 - // 内部格式请参考《beginWork》这章 - beginWork:{} -} -``` - -#### wrappers - -wrappers数组中,只允许存入满足以下格式的对象: - -```json -wrappers:[ - { - // id,传入字符串。 - // 而且:不能与wrappers数组中其他的对象的id属性相同。即必须保证id唯一。 - // 不允许undefined,不允许null。 - "id": "first", - // param,即参数。请看《param》 - // useObjectModel代表value的值是否是“对象模型”。 - // 允许undefined或null,视为{"useObjectModel": false,"value": null} - "param": { - "useObjectModel": false, - "value": "JackMa" - }, - // 传入对象模型,请看《对象模型ObjectModel》 - // 不允许undefined或null - "worker": { - "sameObjectId": 1, - "className:": "schedulingtest.impl.SelectUserByName" - }, - // 传入对象模型 - // 允许undefined与null。如果为两者则使用com.jd.platform.async.callback.DefaultCallback - "callback": { - "sameObjectId": 1 - }, - // wrapper策略 - // 允许undefined与null。如果为两者则使用com.jd.platform.async.wrapper.strategy.WrapperStrategy.DefaultWrapperStrategy - // 即"ALL_DEPENDENCIES_ALL_SUCCESS"与"CHECK_ONE_LEVEL" - "wrapperStrategy": { - // 传入{}键值对,键名为即wrapper的id属性,值为对象模型。 - // 允许undefined和null,两者之意与空键值对{}并无二致 - "dependOnUpWrapperStrategyMapper": null, - // -- 这里不再向历史妥协,舍弃了DependMustStrategyMapper - // 基础策略器,传入对象模型 - // 允许undefined和null,如果是两者则使用"ALL_DEPENDENCIES_ALL_SUCCESS" - "dependenceStrategy": { - "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" - }, - // 跳过策略,传入对象模型 - // 允许undefined和null,如果是两者则使用"CHECK_ONE_LEVEL" - "skipStrategy": { - "constObjectName": "CHECK_ONE_LEVEL" - } - }, - // 是否允许打断,传入boolean值。允许undefined和null,视为false - "allowInterrupt": true, - // 是否启动单wrapper计时,允许undefined和null,视为false - "enableTimeout": true, - // 单wrapper超时时间数值。传入long(int64)值 - // 在enableTimeout为true的情况下不允许为undefined或null或小于等于0的值,否则允许任何值 - "timeoutLength": 50, - // 单wrapper超时时间单位,有以下几个取值:(即java.util.concurrent.TimeUnit的枚举值) - // "NANOSECONDS"、"MICROSECONDS"、"MILLISECONDS" - // "SECONDS"、"MINUTES"、"HOURS"、"DAYS" - // 允许为undefined和null,视为"MILLISECONDS" - // 但是除了undefined、null和以上7个值外,不允许任何值 - "timeoutUnit": "MILLISECONDS" - }, - // 这是第二个wrapper属性,这里可以省略很多选用默认值的属性。 - { - "id": "second", - "param":{ - "useObjectModel": false, - "value":"first" - }, - "worker": { - "className": "schedulingtest.impl.PayTaxes", - "sameObjectId": 2 - }, - callback:{ - "sameObjectId": 2 - } - }, - // 这是第三个属性,这里的"extendConfig"属性是个省力的好东西。 - { - "id": "third", - // 传入存在的id属性 - // 效果是:本wrapper配置将继承此id表示的wrapper配置的所有属性 - // 并且本wrapper可以有选择的覆写配置——只需设置某个对象的值即可。 - // 可以为null或undefined,表示不继承配置 - "extendConfig": "second" - }, - /* , { ... } */ -] -``` - -##### param - -`useObjectModel`属性用于说明`value`属性的所代表的对象类型: - -* 为false:使用json所对应的类型。 -* 为true:使用《对象模型`ObjectModel`》中的我们自定义的对象模型。 - -```json -{ - "useObjectModel": false, - "value": "JackMa" -}, -``` - -```json -{ - "useObjectModel": true, - "value": { - sameObjectId: 3 - } -} -``` - -#### relations - -```json -relation: { - // 可使用 数组(仅当to使用字符串时)、字符串 - // 不允许为null或undefined,如果两个wrapper之间无关系,宁可不写这整个对象。 - from: "", - // 可使用 数组(仅当from使用字符串时)、字符串 - // 不允许为null或undefined,如果两个wrapper之间无关系,宁可不写这整个对象。 - to: "" -} -``` - -`from`和`to`两个属性传入的字符串,必须是在`wrappers`数组中所含有的`id`属性。 - -这两个属性只有在对方为字符串时,才能设置自己为数组,以表示“一对多”关系。 - -#### beginWork - -```json -beginWork: { - // 设置全组超时时间数值,使用long(int64)值 - // 可以传入<=0的值表示不限制超时时间。 - // 允许为null、undefined,视为不限制超时时间。 - timeoutLength: 100, - // 全组超时时间单位,有以下几个取值:(即java.util.concurrent.TimeUnit的枚举值) - // "NANOSECONDS"、"MICROSECONDS"、"MILLISECONDS" - // "SECONDS"、"MINUTES"、"HOURS"、"DAYS" - // 允许为undefined和null,视为"MILLISECONDS" - // 但是除了undefined、null和以上7个值外,不允许任何值 - timeoutUnit: "MILLISECONDS", - // 传入的启动wrapper的id数组,不允许null、undefined。可以为空,但是这不符合常理。 - wrappers: [ - "first" - ], - // 执行的线程池 - // 可以传入"COMMON_POOL",代表使用asyncTool的默认线程池。 - // 可以为null或undefined,视为"COMMON_POOL" - // 也可以以如下格式传入全限定名+字段/方法名字符串,则使用反射调取该字段的所指向的ExecutorService: - // 全限定名#字段名 或 全限定名##方法名 - executor: "COMMON_POOL" -} -``` - -### 其他特殊格式 - -#### 对象模型`ObjectModel` - -* 有时我们需要指定接口实现类,或者是默认的接口实现对象,并自定义这些对象的属性。 - -* 或是指定多个json字段代表一个共同的对象。 - -那么,我们就需要高度自定义此对象的属性,并封装为`com.jd.platform.async.scheduling.model.ObjectModel`类。 - -```json -{ - // 如果需要使用asyncTool指名道姓的常量实现对象,则请见下方《常量对象规范》,并在这里传入名称字符串。 - // 当设置了该属性时,其他的属性均会被忽视。 - "constObjectName": "NOT_SKIP", - // 如果希望指定多个json字段代表一个共同的对象: - // 则将其设为相同的id。 - // 如果设置了该属性,其他的属性均会被忽视。(优先级低于constObjectName) - "sameObjectId": 1, - // 提供类的全限定名字符串,将调用无参构造方法进行初始化 - // 如果constObjectName设置了非null且非undefined值,则此值允许为null或undefined - // 如果sameObjectId设置了非null且非undefined值,则id相同的对象模型中允许且只允许一个值为非null或非undefined,其他的都必须为null或undefined - "className": "your.package.name.YourKlassName", - // 初始化后,会根据该值来修改对象属性 - // 允许为null或undefined,表示不额外设置属性 - - "properties": { - // 其中的键值对为各字段名。这些字段需要有getter、setter方法。 - "myIntegerField": 123123 - } -} -``` - -##### 常量对象大全 - -在`constObjectName`属性中设置,用以下字符串代表如下对象: - -| 对象名字符串 | 常量值 | -| -------------------------------- | ------------------------------------------------------- | -| `"NOT_SKIP"` | `SkipStrategy.NOT_SKIP` | -| `"CHECK_ONE_LEVEL"` | `SkipStrategy.CHECK_ONE_LEVEL` | -| `"ALL_DEPENDENCIES_ALL_SUCCESS"` | `DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS` | -| `"ALL_DEPENDENCIES_ANY_SUCCESS"` | `DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS` | -| `"ALL_DEPENDENCIES_NONE_FAILED"` | ` DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED` | -| `"SUCCESS_CONTINUE"` | `DependOnUpWrapperStrategy.SUCCESS_CONTINUE` | -| `"SUCCESS_START_INIT_CONTINUE"` | `DependOnUpWrapperStrategy.SUCCESS_START_INIT_CONTINUE` | -| `"PRINT_EXCEPTION_STACK_TRACE"` | `ICallback.PRINT_EXCEPTION_STACK_TRACE` | +> `com.jd.platform.async.openutil.*` -详见请参考`com.jd.platform.async.scheduling.model.Constants`类中的具体代码。 +* `BiInt` 一个表示两个int值的实体类,内含缓冲区间、默认比较器,适合拿来当2维索引。 diff --git a/asyncTool-core/pom.xml b/asyncTool-core/pom.xml index 5ed85e0..0838a91 100644 --- a/asyncTool-core/pom.xml +++ b/asyncTool-core/pom.xml @@ -5,7 +5,7 @@ asyncTool com.jd.platform - 1.5.0-SNAPSHOT + 1.5.1-SNAPSHOT 4.0.0 @@ -20,7 +20,7 @@ com.jd.platform asyncTool-openutil - 1.5.0-SNAPSHOT + 1.5.1-SNAPSHOT diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java b/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java new file mode 100644 index 0000000..5532b74 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java @@ -0,0 +1,19 @@ +package com.jd.platform.async.exception; + +/** + * 整组取消,设置该异常。 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午6:12 + */ +public class CancelSkippedException extends SkippedException { + public CancelSkippedException() { + } + + public CancelSkippedException(String message) { + super(message); + } + + public CancelSkippedException(String message, long skipAt) { + super(message, skipAt); + } +} diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java b/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java index 8d820e0..1c38e7a 100644 --- a/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java @@ -1,5 +1,7 @@ package com.jd.platform.async.exception; +import com.jd.platform.async.executor.timer.SystemClock; + /** * 如果任务在执行之前,自己后面的任务已经执行完或正在被执行,则抛该exception * @@ -7,11 +9,22 @@ package com.jd.platform.async.exception; * @version 1.0 */ public class SkippedException extends RuntimeException { + private final long skipAt; + public SkippedException() { - super(); + this(null); } public SkippedException(String message) { + this(message, SystemClock.now()); + } + + public SkippedException(String message, long skipAt) { super(message); + this.skipAt = skipAt; + } + + public long getSkipAt() { + return skipAt; } } diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java index 3232946..c0c3d88 100644 --- a/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java @@ -4,15 +4,15 @@ package com.jd.platform.async.executor; import com.jd.platform.async.callback.DefaultGroupCallback; import com.jd.platform.async.callback.IGroupCallback; import com.jd.platform.async.executor.timer.SystemClock; +import com.jd.platform.async.worker.OnceWork; import com.jd.platform.async.wrapper.WorkerWrapper; import com.jd.platform.async.wrapper.WorkerWrapperGroup; +import com.sun.istack.internal.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** @@ -26,20 +26,65 @@ public class Async { // ========================= 任务执行核心代码 ========================= /** - * 出发点 + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。 + */ + public static OnceWork work(long timeout, + Collection> workerWrappers) { + return work(timeout, getCommonPool(), workerWrappers); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 可变参式传入。使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。 + */ + public static OnceWork work(long timeout, + WorkerWrapper... workerWrappers) { + return work(timeout, getCommonPool(), workerWrappers); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 可变参式传入。使用uuid作为工作id。 + */ + public static OnceWork work(long timeout, + ExecutorService executorService, + WorkerWrapper... workerWrappers) { + return work(timeout, executorService, Arrays.asList( + Objects.requireNonNull(workerWrappers, "workerWrappers array is null"))); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 省略工作id,使用uuid。 + */ + public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers) { + return work(timeout, executorService, workerWrappers, UUID.randomUUID().toString()); + } + + /** + * 核心方法。 + * 该方法不是同步阻塞执行的。如果想要同步阻塞执行,则调用返回值的{@link OnceWork#awaitFinish()}即可。 * - * @return 只要执行未超时,就返回true。 + * @param timeout 全组超时时间 + * @param executorService 执行线程池 + * @param workerWrappers 任务容器集合 + * @param workId 本次工作id + * @return 返回 {@link OnceWork}封装对象。 */ - public static boolean beginWork(long timeout, - ExecutorService executorService, - Collection> workerWrappers) - throws InterruptedException { - if (workerWrappers == null || workerWrappers.size() == 0) { - return false; + public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers, + String workId) { + if (workerWrappers == null || workerWrappers.isEmpty()) { + return OnceWork.emptyWork(workId); } //保存上次执行的线程池变量(为了兼容以前的旧功能) - Async.lastExecutorService = Objects.requireNonNull(executorService, "ExecutorService is null ! "); - WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout); + Async.lastExecutorService.set(Objects.requireNonNull(executorService, "ExecutorService is null ! ")); + final WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout); + final OnceWork.Impl onceWork = new OnceWork.Impl(group, workId); group.addWrapper(workerWrappers); workerWrappers.forEach(wrapper -> { if (wrapper == null) { @@ -47,38 +92,24 @@ public class Async { } executorService.submit(() -> wrapper.work(executorService, timeout, group)); }); - return group.awaitFinish(); - //处理超时的逻辑被移动到了WrapperEndingInspector中。 - } - - /** - * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL - */ - public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) - throws ExecutionException, InterruptedException { - if (workerWrapper == null || workerWrapper.length == 0) { - return false; - } - Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet()); - //noinspection unchecked - return beginWork(timeout, executorService, workerWrappers); + return onceWork; } /** - * 同步阻塞,直到所有都完成,或失败 + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 */ - public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { - return beginWork(timeout, getCommonPool(), workerWrapper); - } - @SuppressWarnings("unused") + @Deprecated public static void beginWorkAsync(long timeout, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) { beginWorkAsync(timeout, getCommonPool(), groupCallback, workerWrapper); } /** * 异步执行,直到所有都完成,或失败后,发起回调 + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 */ + @Deprecated public static void beginWorkAsync(long timeout, ExecutorService executorService, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) { if (groupCallback == null) { groupCallback = new DefaultGroupCallback(); @@ -129,7 +160,7 @@ public class Async { * 该线程池将会给线程取名为asyncTool-commonPool-thread-0(数字不重复)。 *

*/ - private static ThreadPoolExecutor COMMON_POOL; + private static volatile ThreadPoolExecutor COMMON_POOL; /** * 在以前(及现在)的版本中: @@ -137,7 +168,7 @@ public class Async { *

* 注意,这里是个static,也就是只能有一个线程池。用户自定义线程池时,也只能定义一个 */ - private static volatile ExecutorService lastExecutorService; + private static final AtomicReference lastExecutorService = new AtomicReference<>(null); /** * 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。 @@ -155,9 +186,11 @@ public class Async { new ThreadFactory() { private final AtomicLong threadCount = new AtomicLong(0); + @SuppressWarnings("NullableProblems") @Override public Thread newThread(Runnable r) { - Thread t = new Thread(r, "asyncTool-commonPool-thread-" + threadCount.getAndIncrement()); + Thread t = new Thread(r, + "asyncTool-commonPool-thread-" + threadCount.getAndIncrement()); t.setDaemon(true); return t; } @@ -189,6 +222,7 @@ public class Async { * @param now 是否立即关闭 * @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。 */ + @SuppressWarnings("unused") public static synchronized boolean shutDownCommonPool(boolean now) { if (COMMON_POOL == null) { return false; @@ -203,6 +237,51 @@ public class Async { return true; } + // ========================= deprecated ========================= + + /** + * 同步执行一次任务。 + * + * @return 只要执行未超时,就返回true。 + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, + ExecutorService executorService, + Collection> workerWrappers) + throws InterruptedException { + final OnceWork work = work(timeout, executorService, workerWrappers); + work.awaitFinish(); + return work.hasTimeout(); + } + + /** + * 同步执行一次任务。 + * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) + throws ExecutionException, InterruptedException { + if (workerWrapper == null || workerWrapper.length == 0) { + return false; + } + Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet()); + //noinspection unchecked + return beginWork(timeout, executorService, workerWrappers); + } + + /** + * 同步阻塞,直到所有都完成,或失败 + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { + return beginWork(timeout, getCommonPool(), workerWrapper); + } + /** * 关闭上次使用的线程池 * @@ -214,8 +293,9 @@ public class Async { */ @Deprecated public static void shutDown() { - if (lastExecutorService != COMMON_POOL) { - shutDown(lastExecutorService); + final ExecutorService last = lastExecutorService.get(); + if (last != COMMON_POOL) { + shutDown(last); } } @@ -226,7 +306,7 @@ public class Async { * @deprecated 没啥用的方法,要关闭线程池还不如直接调用线程池的关闭方法,避免歧义。 */ @Deprecated - public static void shutDown(ExecutorService executorService) { + public static void shutDown(@Nullable ExecutorService executorService) { if (executorService != null) { executorService.shutdown(); } diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java index 01778a2..2dc1829 100644 --- a/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java @@ -65,7 +65,7 @@ public class PollingCenter { thread.setDaemon(true); return thread; }, - 4, + 1, TimeUnit.MILLISECONDS, 1024); diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java new file mode 100644 index 0000000..611638b --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java @@ -0,0 +1,375 @@ +package com.jd.platform.async.worker; + +import com.jd.platform.async.executor.timer.SystemClock; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; + +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 一次工作结果的总接口。 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午3:22 + */ +public interface OnceWork { + /** + * 返回唯一的workId + */ + String workId(); + + /** + * 判断是否结束。因超时而结束也算结束。 + */ + boolean isFinish(); + + /** + * 同步等待到结束。 + */ + void awaitFinish() throws InterruptedException; + + /** + * 判断是否超时 + * + * @return 如果尚未结束或已结束但未超时,返回false。已结束且已经超时返回true。 + */ + boolean hasTimeout(); + + /** + * 判断是否全部wrapper都处于 执行成功 或 跳过。 + * + * @return 如果已经结束,所有wrapper都成功或跳过返回true,否则返回false。如果尚未结束,返回false。 + */ + default boolean allSuccess() { + if (!isFinish()) { + return false; + } + return getWrappers().values().stream().allMatch(wrapper -> { + final ResultState state = wrapper.getWorkResult().getResultState(); + return state == ResultState.SUCCESS || state == ResultState.DEFAULT; + }); + } + + /** + * 获取全部参与到工作中的wrapper。 + */ + Map> getWrappers(); + + /** + * 获取{@link WorkResult#getResultState()}为{@link ResultState#SUCCESS}的wrapper。 + */ + default Map> getSuccessWrappers() { + return getWrappersOfState(ResultState.SUCCESS); + } + + /** + * 获取状态于这些state中的wrapper。 + * + * @param ofState 状态列表 + * @return 返回Map + */ + default Map> getWrappersOfState(ResultState... ofState) { + final HashSet states = new HashSet<>(Arrays.asList(ofState)); + return getWrappers().entrySet().stream() + .filter(entry -> states.contains(entry.getValue().getWorkResult().getResultState())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + /** + * 获取启动时间 + */ + long getStartTime(); + + /** + * 获取结束时间 + * + * @return 如果超时,返回超时的时刻。如果尚未结束,则抛出异常。 + * @throws IllegalStateException 尚未结束,抛出异常。 + */ + long getFinishTime(); + + /** + * @return 已经取消完成 + */ + boolean isCancelled(); + + /** + * @return 是否正在取消中 + */ + boolean isWaitingCancel(); + + /** + * 请求异步取消。 + */ + void pleaseCancel(); + + /** + * 同步等待取消完成。 + */ + default void pleaseCancelAndAwaitFinish() throws InterruptedException { + if (!isCancelled() && !isWaitingCancel()) { + pleaseCancel(); + } + awaitFinish(); + } + + /** + * @return 返回 {@link AsFuture}封装对象。 + */ + default AsFuture asFuture() { + return new AsFuture(this, limitTime -> limitTime / 16); + } + + /** + * @param sleepCheckInterval 为防止线程爆炸,在{@link Future#get(long, TimeUnit)}方法时使用隔一段时间检查一次。 + * 该Function的参数为总超时毫秒值,返回值为检查时间间隔。 + * @return 返回 {@link AsFuture}封装对象。 + */ + default AsFuture asFuture(Function sleepCheckInterval) { + return new AsFuture(this, sleepCheckInterval); + } + + // static + + /** + * 空任务 + */ + static OnceWork emptyWork(String workId) { + return new EmptyWork(workId); + } + + // class + + class AsFuture implements Future>> { + private final OnceWork onceWork; + private final Function sleepCheckInterval; + + private AsFuture(OnceWork onceWork, Function sleepCheckInterval) { + this.onceWork = onceWork; + this.sleepCheckInterval = sleepCheckInterval; + } + + /** + * 同步等待取消 + * + * @param ignore__mayInterruptIfRunning 该参数将被无视。因为暂未实现“修改允许打断属性”功能。 // todo 等待实现 + */ + @Override + public boolean cancel(boolean ignore__mayInterruptIfRunning) { + try { + onceWork.pleaseCancelAndAwaitFinish(); + } catch (InterruptedException e) { + throw new RuntimeException("", e); + } + return true; + } + + @Override + public boolean isCancelled() { + return onceWork.isCancelled(); + } + + @Override + public boolean isDone() { + return onceWork.isFinish(); + } + + @Override + public Map> get() throws InterruptedException, ExecutionException { + if (!onceWork.isFinish()) { + onceWork.awaitFinish(); + } + return onceWork.getWrappers(); + } + + /** + * 避免线程爆炸,该方法不予单独开线程,而是单线程{@link Thread#sleep(long)}每睡一段时间检查一次。 + */ + @Override + public Map> get(long timeout, + @SuppressWarnings("NullableProblems") TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + final long millis = Objects.requireNonNull(unit).toMillis(timeout); + final long interval = Math.max(1, Math.min(millis, sleepCheckInterval.apply(millis))); + for (int i = 0; interval * i < millis; i++) { + if (onceWork.isFinish()) { + return onceWork.getWrappers(); + } + Thread.sleep(interval); + } + throw new TimeoutException( + "onceWork.asFuture.get(long,TimeUnit) out of time limit(" + + timeout + "," + unit + ") , this is " + this); + } + + @Override + public String toString() { + return "(asFuture from " + this + ")"; + } + } + + abstract class AbstractOnceWork implements OnceWork { + protected final String workId; + + public AbstractOnceWork(String workId) { + this.workId = workId; + } + + @Override + public String workId() { + return workId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OnceWork)) { + return false; + } + OnceWork _o = (OnceWork) o; + return Objects.equals(_o.workId(), this.workId()); + } + + @Override + public int hashCode() { + return workId().hashCode(); + } + + @Override + public String toString() { + final boolean finish; + final StringBuilder sb = new StringBuilder(48) + .append(this.getClass().getSimpleName()) + .append("{isFinish=").append(finish = isFinish()) + .append(", hasTimeout=").append(hasTimeout()) + .append(", allSuccess=").append(allSuccess()) + .append(", getStartTime=").append(getStartTime()) + .append(", isCancelled=").append(isCancelled()) + .append(", isWaitingCancel=").append(isWaitingCancel()); + if (finish) { + sb.append(", getFinishTime=").append(getFinishTime()); + } + return sb + .append(", wrappers::getId=").append(getWrappers().keySet()) + .append('}').toString(); + } + } + + class Impl extends AbstractOnceWork { + protected final WorkerWrapperGroup group; + + public Impl(WorkerWrapperGroup group, String workId) { + super(workId); + this.group = group; + } + + @Override + public boolean isFinish() { + return group.getEndCDL().getCount() > 0; + } + + @Override + public void awaitFinish() throws InterruptedException { + group.getEndCDL().await(); + } + + @Override + public boolean hasTimeout() { + return group.getAnyTimeout().get(); + } + + @Override + public Map> getWrappers() { + return group.getForParamUseWrappers(); + } + + @Override + public long getStartTime() { + return group.getGroupStartTime(); + } + + @Override + public long getFinishTime() { + if (isFinish()) { + throw new IllegalStateException("work not finish."); + } + return group.getFinishTime(); + } + + @Override + public boolean isCancelled() { + return group.isCancelled(); + } + + @Override + public boolean isWaitingCancel() { + return group.isWaitingCancel(); + } + + @Override + public void pleaseCancel() { + group.pleaseCancel(); + } + } + + class EmptyWork extends AbstractOnceWork { + private final long initTime = SystemClock.now(); + + public EmptyWork(String workId) { + super(workId); + } + + @Override + public boolean isFinish() { + return true; + } + + @Override + public void awaitFinish() { + // do nothing + } + + @Override + public boolean hasTimeout() { + return false; + } + + @Override + public Map> getWrappers() { + return Collections.emptyMap(); + } + + @Override + public long getStartTime() { + return initTime; + } + + @Override + public long getFinishTime() { + return initTime; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isWaitingCancel() { + return false; + } + + @Override + public void pleaseCancel() { + } + + @Override + public String toString() { + return "(it's empty work)"; + } + } +} diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java index 64331d4..51fcdcc 100755 --- a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java @@ -1,5 +1,6 @@ package com.jd.platform.async.wrapper; +import com.jd.platform.async.exception.CancelSkippedException; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.callback.DefaultCallback; @@ -32,7 +33,7 @@ import static com.jd.platform.async.wrapper.WorkerWrapper.State.STARTED; import static com.jd.platform.async.wrapper.WorkerWrapper.State.SUCCESS; import static com.jd.platform.async.wrapper.WorkerWrapper.State.WORKING; import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_all; -import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_checkTimeoutAllowStates; +import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_beforeWorkingEnd; import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_notWorked; import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_skipOrAfterWork; import static com.jd.platform.async.wrapper.WorkerWrapper.State.*; @@ -96,7 +97,6 @@ public abstract class WorkerWrapper { */ protected final AtomicReference> workResult = new AtomicReference<>(null); - WorkerWrapper(String id, IWorker worker, ICallback callback, @@ -231,6 +231,16 @@ public abstract class WorkerWrapper { } while (true); } + /** + * 直接取消wrapper运行。 + * 如果状态在 {@link State#states_of_beforeWorkingEnd}中,则调用 {@link #fastFail(boolean, Exception, boolean)}。 + */ + public void cancel() { + if (State.setState(state, states_of_beforeWorkingEnd, SKIP, null)) { + fastFail(false, new CancelSkippedException(), true); + } + } + public WrapperStrategy getWrapperStrategy() { return wrapperStrategy; } @@ -298,15 +308,20 @@ public abstract class WorkerWrapper { if (isState(state, BUILDING)) { throw new IllegalStateException("wrapper can't work because state is BUILDING ! wrapper is " + this); } - //总的已经超时了,就快速失败,进行下一个 + // 判断是否整组取消 + if (group.isWaitingCancel() || group.isCancelled()) { + cancel(); + return; + } + // 总的已经超时了,就快速失败,进行下一个 if (remainTime <= 0) { - if (setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { + if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) { __function__fastFail_callbackResult$false_beginNext.accept(true, null); } return; } - //如果自己已经执行过了。 - //可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 + // 如果自己已经执行过了。 + // 可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 final AtomicReference oldStateRef = new AtomicReference<>(null); if (!setState(state, states_of_notWorked, STARTED, oldStateRef::set)) { return; @@ -317,7 +332,7 @@ public abstract class WorkerWrapper { callback.begin(); } catch (Exception e) { // callback.begin 发生异常 - if (setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { + if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) { __function__fastFail_callbackResult$false_beginNext.accept(false, e); } return; @@ -341,7 +356,8 @@ public abstract class WorkerWrapper { } // 如果是由其他wrapper调用而运行至此,则使用策略器决定自己的行为 - DependenceAction.WithProperty judge = wrapperStrategy.judgeAction(getDependWrappers(), this, fromWrapper); + DependenceAction.WithProperty judge = + wrapperStrategy.judgeAction(getDependWrappers(), this, fromWrapper); switch (judge.getDependenceAction()) { case TAKE_REST: return; @@ -360,7 +376,9 @@ public abstract class WorkerWrapper { return; case JUDGE_BY_AFTER: default: - throw new Error("策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + ",fromWrapper=" + fromWrapper); + throw new IllegalStateException( + "策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + + ",fromWrapper=" + fromWrapper); } } catch (Exception e) { // wrapper本身抛出了不该有的异常 @@ -389,7 +407,6 @@ public abstract class WorkerWrapper { } finally { doWorkingThread.set(null); } - } /** @@ -458,8 +475,6 @@ public abstract class WorkerWrapper { } - // ========== private ========== - // ========== hashcode and equals ========== @Override @@ -645,7 +660,7 @@ public abstract class WorkerWrapper { static final State[] states_of_skipOrAfterWork = new State[]{SKIP, AFTER_WORK}; - static final State[] states_of_checkTimeoutAllowStates = new State[]{INIT, STARTED, WORKING}; + static final State[] states_of_beforeWorkingEnd = new State[]{INIT, STARTED, WORKING}; static final State[] states_all = new State[]{BUILDING, INIT, STARTED, WORKING, AFTER_WORK, SUCCESS, ERROR, SKIP}; diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java index e3a0b3e..5ae630b 100644 --- a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java @@ -1,5 +1,6 @@ package com.jd.platform.async.wrapper; +import com.jd.platform.async.exception.CancelSkippedException; import com.jd.platform.async.executor.PollingCenter; import java.util.Collection; @@ -8,9 +9,11 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; +import com.jd.platform.async.executor.timer.SystemClock; import com.jd.platform.async.openutil.timer.*; /** @@ -35,8 +38,23 @@ public class WorkerWrapperGroup { * 当全部wrapper都调用结束,它会countDown */ private final CountDownLatch endCDL = new CountDownLatch(1); - + /** + * 检测到超时,此标记变量将为true。 + */ private final AtomicBoolean anyTimeout = new AtomicBoolean(false); + /** + * 结束时间 + */ + private volatile long finishTime = -1L; + /** + * 取消任务状态 + * 0 - not cancel , 1 - waiting cancel , 2 - already cancel + */ + private final AtomicInteger cancelState = new AtomicInteger(); + + public static final int NOT_CANCEL = 0; + public static final int WAITING_CANCEL = 1; + public static final int ALREADY_CANCEL = 2; public WorkerWrapperGroup(long groupStartTime, long timeoutLength) { this.groupStartTime = groupStartTime; @@ -47,7 +65,6 @@ public class WorkerWrapperGroup { Objects.requireNonNull(wrapper).forEach(this::addWrapper); } - @SuppressWarnings("unused") public void addWrapper(WorkerWrapper... wrappers) { for (WorkerWrapper wrapper : Objects.requireNonNull(wrappers)) { addWrapper(wrapper); @@ -64,14 +81,33 @@ public class WorkerWrapperGroup { return forParamUseWrappers; } - /** - * 同步等待这组wrapper执行完成 - * - * @return false代表有wrapper超时了。true代表全部wrapper没有超时。 - */ - public boolean awaitFinish() throws InterruptedException { - endCDL.await(); - return !anyTimeout.get(); + public CountDownLatch getEndCDL() { + return endCDL; + } + + public long getGroupStartTime() { + return groupStartTime; + } + + public AtomicBoolean getAnyTimeout() { + return anyTimeout; + } + + public long getFinishTime() { + return finishTime; + } + + public boolean isCancelled() { + return cancelState.get() == ALREADY_CANCEL; + } + + public boolean isWaitingCancel() { + return cancelState.get() == WAITING_CANCEL; + } + + @SuppressWarnings("UnusedReturnValue") + public boolean pleaseCancel() { + return cancelState.compareAndSet(NOT_CANCEL, WAITING_CANCEL); } public class CheckFinishTask implements TimerTask { @@ -85,11 +121,19 @@ public class WorkerWrapperGroup { } AtomicBoolean hasTimeout = new AtomicBoolean(false); // 记录正在运行中的wrapper里,最近的限时时间。 - AtomicLong minDaley = new AtomicLong(Long.MAX_VALUE); + final AtomicLong minDaley = new AtomicLong(Long.MAX_VALUE); final Collection> values = forParamUseWrappers.values(); - final Stream> stream = values.size() > 1024 ? values.parallelStream() : values.stream(); - boolean allFinish = stream - // 处理超时 + final Stream> stream = values.size() > 128 ? values.parallelStream() : values.stream(); + final boolean needCancel = cancelState.get() == WAITING_CANCEL; + boolean allFinish_and_notNeedCancel = stream + // 需要取消的话就取消 + .peek(wrapper -> { + if (needCancel) { + wrapper.cancel(); + } + }) + // 检查超时并保存最近一次限时时间 + // 当需要取消时,才会不断遍历。如果不需要取消,则计算一次(或并行流中多次)就因allMatch不满足而退出了。 .peek(wrapper -> { // time_diff : // -1 -> already timeout ; @@ -102,27 +146,39 @@ public class WorkerWrapperGroup { if (time_diff == 0) { return; } + // use CAS and SPIN for thread safety in parallelStream . do { long getMinDaley = minDaley.get(); - if (getMinDaley <= time_diff || minDaley.compareAndSet(getMinDaley, time_diff)) { - return; + // 需要设置最小时间,但是cas失败,则自旋 + if (getMinDaley <= time_diff && !minDaley.compareAndSet(getMinDaley, time_diff)) { + continue; } + return; } while (true); }) - // 判断是否结束,这里如果还有未结束的wrapper则会提前结束流。 - .allMatch(wrapper -> wrapper.getState().finished()); + // 判断是否不需要取消且全部结束 + // 在不需要取消时,这里如果还有未结束的wrapper则会提前结束流并返回false + // 在需要取消时,会全部遍历一遍并取消掉已经进入链路的wrapper + .allMatch(wrapper -> !needCancel && wrapper.getState().finished()); long getMinDaley = minDaley.get(); + // 如果本次取消掉了任务,或是所有wrapper都已经完成 + // ( ps : 前后两条件在这里是必定 一真一假 或 两者全假 ) + if (needCancel || allFinish_and_notNeedCancel) { + // 如果这次进行了取消,则设置取消状态为已完成 + if (needCancel) { + cancelState.set(ALREADY_CANCEL); + } + anyTimeout.set(hasTimeout.get()); + finishTime = SystemClock.now(); + endCDL.countDown(); + } // 如果有正在运行的wrapper - if (!allFinish) { + else { // 如果有正在WORKING的wrapper,则计算一下限时时间,限时完成后轮询它。 if (getMinDaley != Long.MAX_VALUE) { PollingCenter.getInstance().checkGroup(this, getMinDaley); } } - if (allFinish) { - anyTimeout.set(hasTimeout.get()); - endCDL.countDown(); - } } // hashCode and equals will called WorkerWrapperGroup.this diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java index c1229fc..044c8d9 100644 --- a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java @@ -33,7 +33,9 @@ public interface WrapperStrategy extends DependenceStrategy, SkipStrategy { */ @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated - DependMustStrategyMapper getDependMustStrategyMapper(); + default DependMustStrategyMapper getDependMustStrategyMapper() { + return null; + } /** * 底层全局策略。 diff --git a/asyncTool-core/src/test/java/v15/cases/Case0.java b/asyncTool-core/src/test/java/v15/cases/Case0.java new file mode 100644 index 0000000..52fa6cf --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case0.java @@ -0,0 +1,37 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +import java.util.concurrent.ExecutionException; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:10 + */ +class Case0 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").build(); + WorkerWrapper c = builder("C").build(); + try { + Async.work(100, a, b, c).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case01.java b/asyncTool-core/src/test/java/v15/cases/Case01.java new file mode 100644 index 0000000..cf1194f --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case01.java @@ -0,0 +1,37 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:13 + */ +class Case01 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").depends(a).build(); + WorkerWrapper c = builder("C").depends(a).build(); + WorkerWrapper f = builder("F").depends(b, c).build(); + try { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=C) is working + wrapper(id=B) is working + wrapper(id=F) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case02.java b/asyncTool-core/src/test/java/v15/cases/Case02.java new file mode 100644 index 0000000..0c9a65d --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case02.java @@ -0,0 +1,38 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:15 + */ +class Case02 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper f = builder("F").build(); + WorkerWrapper a = builder("A") + .nextOf(builder("B").nextOf(f).build()) + .nextOf(builder("C").nextOf(f).build()) + .build(); + try { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + wrapper(id=F) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case1.java b/asyncTool-core/src/test/java/v15/cases/Case1.java index 11075ba..c0a1b74 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case1.java +++ b/asyncTool-core/src/test/java/v15/cases/Case1.java @@ -43,8 +43,8 @@ class Case1 { ) .build(); try { - Async.beginWork(1000, a, d); - } catch (ExecutionException | InterruptedException e) { + Async.work(1000, a, d).awaitFinish(); + } catch (InterruptedException e) { e.printStackTrace(); } /* 输出: diff --git a/asyncTool-core/src/test/java/v15/cases/Case10.java b/asyncTool-core/src/test/java/v15/cases/Case10.java new file mode 100644 index 0000000..2f6fcc6 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case10.java @@ -0,0 +1,72 @@ +package v15.cases; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.OnceWork; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +import java.util.concurrent.ExecutionException; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/26-下午4:07 + */ +class Case10 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })) + .allowInterrupt(true); + } + + /** + * A(10ms) ==> B(10ms) ==> C(10ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final WorkerWrapper c; + final WorkerWrapper b; + final WorkerWrapper a = builder("A", 10) + .nextOf(b = builder("B", 10) + .nextOf(c = builder("C", 10).build()) + .build()) + .build(); + final OnceWork onceWork = Async.work(40, a); + Thread.sleep(25); + onceWork.pleaseCancelAndAwaitFinish(); + System.out.println("任务b信息 " + b); + System.out.println("任务c信息 " + c); + System.out.println("OnceWork信息 " + onceWork); + /* + 可以看到C的state为SKIP,workResult.ex为CancelSkippedException,即被取消了。 + 不过有时程序运行慢,导致B被取消了,那么C就不会执行,其状态就为INIT了。 + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case2.java b/asyncTool-core/src/test/java/v15/cases/Case2.java index bd2758d..1c930c7 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case2.java +++ b/asyncTool-core/src/test/java/v15/cases/Case2.java @@ -46,7 +46,7 @@ class Case2 { .id("id:200").worker(new AddWork()).param(200).build(); WorkerWrapper add = WorkerWrapper.builder().id("id:add") .worker(new AddWork("id:100", "id:200")).depends(wrapper100, wrapper200).build(); - Async.beginWork(20,wrapper100,wrapper200); + Async.work(20,wrapper100,wrapper200).awaitFinish(); System.out.println(add.getWorkResult()); // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} } diff --git a/asyncTool-core/src/test/java/v15/cases/Case3.java b/asyncTool-core/src/test/java/v15/cases/Case3.java index 01e82a5..8c5dc98 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case3.java +++ b/asyncTool-core/src/test/java/v15/cases/Case3.java @@ -45,7 +45,7 @@ class Case3 { // 这里用线程数较少的线程池做示例,对于ALL_DEPENDENCIES_ANY_SUCCESS“仅需一个”的效果会好一点 ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, a); + Async.work(1000, pool, a).awaitFinish(); } finally { pool.shutdown(); } diff --git a/asyncTool-core/src/test/java/v15/cases/Case4.java b/asyncTool-core/src/test/java/v15/cases/Case4.java index 657f7a7..e78a599 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case4.java +++ b/asyncTool-core/src/test/java/v15/cases/Case4.java @@ -51,7 +51,7 @@ class Case4 { } ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, a); + Async.work(1000, pool, a).awaitFinish(); } finally { pool.shutdown(); } diff --git a/asyncTool-core/src/test/java/v15/cases/Case5.java b/asyncTool-core/src/test/java/v15/cases/Case5.java index 7a96e6d..5b3c99b 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case5.java +++ b/asyncTool-core/src/test/java/v15/cases/Case5.java @@ -56,7 +56,7 @@ class Case5 { WorkerWrapper start = builder("start").nextOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).build(); ExecutorService pool = Executors.newFixedThreadPool(2); try { - Async.beginWork(1000, pool, start); + Async.work(1000, pool, start).awaitFinish(); } finally { pool.shutdown(); } diff --git a/asyncTool-core/src/test/java/v15/cases/Case6.java b/asyncTool-core/src/test/java/v15/cases/Case6.java index 2368220..9de0258 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case6.java +++ b/asyncTool-core/src/test/java/v15/cases/Case6.java @@ -46,7 +46,7 @@ class Case6 { .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) .wrapper(b) .end().build(); - Async.beginWork(1000, a); + Async.work(1000, a).awaitFinish(); System.out.println(a.getWorkResult()); System.out.println(b.getWorkResult()); /* 输出: diff --git a/asyncTool-core/src/test/java/v15/cases/Case7.java b/asyncTool-core/src/test/java/v15/cases/Case7.java index 1be8b93..14c2e85 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case7.java +++ b/asyncTool-core/src/test/java/v15/cases/Case7.java @@ -50,7 +50,7 @@ class Case7 { .build(), builder("E", 5).nextOf(d).build() ).build(); - Async.beginWork(1000, a); + Async.work(1000, a).awaitFinish(); /* 输出: wrapper(id=A) is working wrapper(id=E) is working diff --git a/asyncTool-core/src/test/java/v15/cases/Case8.java b/asyncTool-core/src/test/java/v15/cases/Case8.java index c91b83a..bbc5def 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case8.java +++ b/asyncTool-core/src/test/java/v15/cases/Case8.java @@ -55,7 +55,7 @@ class Case8 { .nextOf(builder("C", 20).build()) .build()) .build(); - Async.beginWork(15, a); + Async.work(20, a).awaitFinish(); /* 输出: wrapper(id=A) has begin . wrapper(id=A) is working diff --git a/asyncTool-core/src/test/java/v15/cases/Case9.java b/asyncTool-core/src/test/java/v15/cases/Case9.java index a55fe08..d58c4d7 100644 --- a/asyncTool-core/src/test/java/v15/cases/Case9.java +++ b/asyncTool-core/src/test/java/v15/cases/Case9.java @@ -51,9 +51,15 @@ class Case9 { // System.out.println(graph); - Async.beginWork(200, w1); + Async.work(200, w1).awaitFinish(); System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); - + /* 输出: + I am IWorker 1 + I am IWorker 2 + Begin work end . + w1 : 省略 + w2 : 省略 + */ } } diff --git a/asyncTool-openutil/pom.xml b/asyncTool-openutil/pom.xml index e5ef877..e0603b8 100644 --- a/asyncTool-openutil/pom.xml +++ b/asyncTool-openutil/pom.xml @@ -5,7 +5,7 @@ asyncTool com.jd.platform - 1.5.0-SNAPSHOT + 1.5.1-SNAPSHOT 4.0.0 diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java index 5f7c749..5902bd6 100644 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java @@ -3,7 +3,7 @@ package com.jd.platform.async.openutil; import java.util.Comparator; /** - * 两个int值。重写了{@link #hashCode()}与{@link #equals(Object)} + * 两个int值包装类。重写了{@link #hashCode()}与{@link #equals(Object)} * * @author create by TcSnZh on 2021/5/16-上午1:50 */ diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java deleted file mode 100644 index 0ed4d44..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/BiTree.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.jd.platform.async.openutil.collection; - -import java.util.Arrays; -import java.util.Collection; -import java.util.function.Function; - -/** - * 二叉树 - * - * @author create by TcSnZh on 2021/5/15-下午8:00 - */ -public interface BiTree extends Tree { - // todo - - /** - * 二叉树节点 - */ - interface BiNode extends Node { - @Override - default BiNode getParent() { - throw new UnsupportedOperationException("Get parent node is not supported."); - } - - BiNode getLeft(); - - BiNode getRight(); - - @Override - default Collection getChildren() { - return Arrays.asList(getLeft(), getRight()); - } - } - - /** - * 返回一个通俗易懂的字符画。 - *

- * 从leetcode上抄的。若有侵权请联系 {@code zh0u.he@qq.com},将会删除。 - * - * @param node 根节点 - * @param provideName 节点显示在图中的名字。 - * @param 根节点泛型 - * @return 返回一个字符画 - */ - static String toPrettyString(N node, Function provideName) { - StringBuilder sb = new StringBuilder(); - //noinspection unchecked - _toPrettyString(node, "", true, sb, (Function) provideName); - return sb.toString(); - } - - /** - * jdk8没有private static,只能加条下划线意思意思了。 - */ - static void _toPrettyString(N node, - String prefix, - boolean isLeft, - StringBuilder sb, - Function provideName) { - if (node == null) { - sb.append("(Empty tree)"); - return; - } - - BiNode right = node.getRight(); - if (right != null) { - _toPrettyString(right, prefix + (isLeft ? "│ " : " "), false, sb, provideName); - } - - sb.append(prefix).append(isLeft ? "└── " : "┌── ").append(provideName.apply(node)).append('\n'); - - BiNode left = node.getLeft(); - if (left != null) { - _toPrettyString(left, prefix + (isLeft ? " " : "│ "), true, sb, provideName); - } - } -} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java deleted file mode 100644 index f5e72dd..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CollisionRangeTable.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.jd.platform.async.openutil.collection; - -/** - * 冲突范围表 - * - * @author create by TcSnZh on 2021/5/16-上午1:36 - */ -public class CollisionRangeTable { - // todo -} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java index 4f326d2..5ac6dc7 100644 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java @@ -1,9 +1,6 @@ package com.jd.platform.async.openutil.collection; -import java.util.AbstractSet; -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; +import java.util.*; /** * @author create by TcSnZh on 2021/5/16-下午11:27 diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java deleted file mode 100644 index b3d6ec3..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Tree.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jd.platform.async.openutil.collection; - -import java.util.Collection; - -/** - * @author create by TcSnZh on 2021/5/15-下午7:58 - */ -public interface Tree { - interface Node { - default Node getParent(){ - throw new UnsupportedOperationException("Get parent node is not supported."); - } - - Collection getChildren(); - } -} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java deleted file mode 100644 index 0c9ad40..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/WheelIterator.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jd.platform.async.openutil.collection; - -import java.util.Iterator; - -/** - * 一个反复循环的迭代器 - * - * @author create by TcSnZh on 2021/5/9-下午6:25 - */ -public interface WheelIterator extends Iterator { - @Override - E next(); - - /** - * 一轮的元素数 - */ - int cycle(); - - @Override - default boolean hasNext() { - return cycle() > 0; - } -} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java deleted file mode 100644 index 2d92fe2..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/concurrent/RangeLock.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.jd.platform.async.openutil.concurrent; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -/** - * 范围锁 todo - * - * @author create by TcSnZh on 2021/5/15-下午6:23 - */ -public interface RangeLock extends Lock { - void lock(int start, int end); - - boolean tryLock(int start, int end); - - boolean tryLock(int start, int end, long time, TimeUnit unit) throws InterruptedException; - - void lockInterruptibly(int start, int end) throws InterruptedException; - - @Override - Condition newCondition(); - - @Override - void unlock(); - - @Override - default void lock() { - lock(Integer.MIN_VALUE, Integer.MAX_VALUE); - } - - @Override - default void lockInterruptibly() throws InterruptedException { - lockInterruptibly(Integer.MIN_VALUE, Integer.MAX_VALUE); - } - - @Override - default boolean tryLock() { - return tryLock(Integer.MIN_VALUE, Integer.MAX_VALUE); - } - - @Override - default boolean tryLock(long time, TimeUnit unit) throws InterruptedException { - return tryLock(Integer.MIN_VALUE, Integer.MAX_VALUE, time, unit); - } -} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java deleted file mode 100644 index 35ecf2a..0000000 --- a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/unsafe/UnsafeUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jd.platform.async.openutil.unsafe; - -import sun.misc.Unsafe; - -import java.lang.reflect.Field; - -/** - * @author create by TcSnZh on 2021/5/16-下午4:36 - */ -@SuppressWarnings({"AlibabaAbstractClassShouldStartWithAbstractNaming", "unused"}) -abstract class UnsafeUtil { - private static final Unsafe unsafe; - - static { - Field theUnsafe; - try { - theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - } catch (NoSuchFieldException e) { - throw new Error(e); - } - theUnsafe.setAccessible(true); - try { - unsafe = (Unsafe) theUnsafe.get(null); - } catch (IllegalAccessException e) { - throw new Error(e); - } - } - - @SuppressWarnings("FinalStaticMethod") - public static final Unsafe getUnsafe() { - return unsafe; - } -} diff --git a/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java index 533485b..bddf07b 100644 --- a/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java +++ b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java @@ -10,7 +10,7 @@ import java.util.Arrays; * * @author create by TcSnZh on 2021/5/16-下午11:25 */ -public class TestGraph { +class TestGraph { public static void main(String[] args) { test_CommonDirectedGraph(); } diff --git a/asyncTool-scheduling/pom.xml b/asyncTool-scheduling/pom.xml deleted file mode 100644 index 7561f8d..0000000 --- a/asyncTool-scheduling/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - asyncTool - com.jd.platform - 1.5.0-SNAPSHOT - - 4.0.0 - - asyncTool-scheduling - - - 8 - 8 - - - - - asyncTool-core - com.jd.platform - 1.5.0-SNAPSHOT - - - com.alibaba - fastjson - 1.2.76 - - - \ No newline at end of file diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java deleted file mode 100644 index 413b609..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/AsyncScheduling.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jd.platform.async.scheduling; - -/** - * 入口方法类 - * - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:05 - */ -public class AsyncScheduling { -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java deleted file mode 100644 index 030804c..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/DefaultSchedulingDrawingsParser.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.jd.platform.async.scheduling.drawings; - -import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; -import com.jd.platform.async.scheduling.model.ObjectModel; -import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Supplier; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:38 - */ -class DefaultSchedulingDrawingsParser implements SchedulingDrawingsParser { - static final DefaultSchedulingDrawingsParser instance = new DefaultSchedulingDrawingsParser(); - - @Override - public SchedulingDrawings parseDrawings(final SchedulingDrawingsModel model) throws IllegalSchedulingPropertyException { - final SchedulingDrawingsImpl drawings = new SchedulingDrawingsImpl(); - final String drawingsName = model.getDrawingsName(); - drawings.setName(drawingsName == null ? UUID.randomUUID().toString() : drawingsName); - final List wrappers = - propRequireNotNull(model.getWrappers(), "wrappers"); - // 缓存WrapperModelMap并检查id是否唯一 - final LinkedHashMap wrapperId2ModelMap = - new LinkedHashMap<>(wrappers.size()); - for (int i = 0; i < wrappers.size(); i++) { - final SchedulingDrawingsModel.WrapperModel wrapper = wrappers.get(i); - // 检查id是否重复,并存入map - final String id = propRequireNotNull(wrapper.getId(), "wrappers[" + i + "].id"); - if (null != wrapperId2ModelMap.put(id, wrapper)) { - throw new IllegalSchedulingPropertyException("Wrapper id \"" + id + "\" duplicate ."); - } - } - // 缓存ObjectModelMap并检查 - final Map wrappersObjectModelMap = wrappersObjectModelMap(wrappers, wrapperId2ModelMap.keySet()); - // 构造wrapperFactory - // todo 还没写完 - return null; - } - - // ========== private method ========== - - - // ========== util static methods ========== - - /** - * 缓存sameObjectId有有效值的ObjectModel,并检查: - * 1. ObjectModel参数是否符合格式规范 - * 2. wrapperStrategy.dependOnUpWrapperStrategyMapper 的键id是否存在于wrappers的id属性集合中 - * - * @param wrappers wrappers属性值的列表 - * @param wrappersIdSet 已经包括wrappers列表中所有id属性的Set - * @return 返回Map的键为ObjectModel的sameObjectId,值为ObjectModel - * @throws IllegalSchedulingPropertyException 格式错误,抛出异常 - */ - protected static Map wrappersObjectModelMap( - List wrappers, - Set wrappersIdSet - ) throws IllegalSchedulingPropertyException { - final LinkedHashMap objectModelMap = new LinkedHashMap<>(); - for (final SchedulingDrawingsModel.WrapperModel wrapper : wrappers) { - final String prefixPropName = "wrappers[id=" + wrapper.getId() + "]"; - final SchedulingDrawingsModel.WrapperModel.ParamModel param = wrapper.getParam(); - // 将param参数中的objectModel检查并存入map - if (param != null && Boolean.TRUE.equals( - propRequireNotNull(param.getUseObjectModel(), prefixPropName + ".param.useObjectModel") - )) { - final Object value = param.getValue(); - if (value instanceof ObjectModel) { - checkAndPutObjectModelHasDifferentId( - (ObjectModel) value, - objectModelMap, - prefixPropName + ".param.value"); - } else { - throw IllegalSchedulingPropertyException.illegalFieldParameter( - prefixPropName + ".param.value", - value, - "it should instanceof ObjectModel." - ); - } - } - // 检查并缓存worker的objectModel - final String _workerPropName = prefixPropName + ".worker"; - checkAndPutObjectModelHasDifferentId(propRequireNotNull(wrapper.getWorker(), _workerPropName), - objectModelMap, _workerPropName); - // 检查并缓存callback的objectModel - checkAndPutObjectModelHasDifferentId(wrapper.getCallback(), objectModelMap, prefixPropName + ".callback"); - // 检查wrapperStrategy - final SchedulingDrawingsModel.WrapperModel.WrapperStrategyModel wrapperStrategy = wrapper.getWrapperStrategy(); - if (wrapperStrategy != null) { - // 检查并缓存dependOnUpWrapperStrategyMapper - final Map dependOnUpWrapperStrategyMapper = - wrapperStrategy.getDependOnUpWrapperStrategyMapper(); - if (dependOnUpWrapperStrategyMapper != null) { - for (Map.Entry entry : - dependOnUpWrapperStrategyMapper.entrySet()) { - final String wrapperId = entry.getKey(); - final String mapperPropName = prefixPropName + ".wrapperStrategy.dependOnUpWrapperStrategyMapper"; - if (!wrappersIdSet.contains(wrapperId)) { - throw IllegalSchedulingPropertyException.illegalFieldParameter( - mapperPropName, - dependOnUpWrapperStrategyMapper, - "the key(wrapperId) of \"" + wrapperId + "\n not in wrappers list" - ); - } - checkAndPutObjectModelHasDifferentId( - propRequireNotNull(entry.getValue(), mapperPropName), - objectModelMap, - mapperPropName - ); - } - } - // 检查并缓存dependenceStrategy - final ObjectModel dependenceStrategy = wrapperStrategy.getDependenceStrategy(); - final String dependenceStrategyPropName = prefixPropName + ".wrapperStrategy.dependenceStrategy"; - if (dependenceStrategy != null) { - checkAndPutObjectModelHasDifferentId( - dependenceStrategy, - objectModelMap, - dependenceStrategyPropName - ); - } - // 检查并缓存skipStrategy - final String skipStrategyPropName = prefixPropName + ".wrapperStrategy.skipStrategy"; - final ObjectModel skipStrategy = wrapperStrategy.getSkipStrategy(); - if (skipStrategy != null) { - checkAndPutObjectModelHasDifferentId( - skipStrategy, - objectModelMap, - skipStrategyPropName); - } - } - } - return objectModelMap; - } - - /** - * 本方法为{@link #wrappersObjectModelMap(List, Set)}的子方法。 - * 用于抽取ObjectModel格式的判断逻辑。 - * - * @param objectModel 对象模型 - * @param objectModelMap 要存入的Map - * @param propNameSup 属性名 - * @throws IllegalSchedulingPropertyException 格式错误,抛出异常 - */ - private static void checkAndPutObjectModelHasDifferentId(ObjectModel objectModel, - Map objectModelMap, - Supplier propNameSup) - throws IllegalSchedulingPropertyException { - final String constObjectName = objectModel.getConstObjectName(); - if (constObjectName != null) { - if (!ObjectModel.containConstObject(constObjectName)) { - final String propName; - throw new IllegalSchedulingPropertyException( - (propNameSup == null || (propName = propNameSup.get()) == null ? "" : "Property " + propName + " ") + - "ObjectModel doesn't have a constant object named \"" - + constObjectName + "\" . objectModel is " + objectModel); - } - return; - } - final Long sameObjectId = objectModel.getSameObjectId(); - if (sameObjectId != null) { - final Map properties = objectModel.getProperties(); - if (objectModelMap.containsKey(sameObjectId) && - // 同一id的对象,其他属性不允许重复设置 - (objectModel.getClassName() != null || properties != null && !properties.isEmpty())) { - throw new IllegalSchedulingPropertyException( - "The objectModel which sameObjectId=" + sameObjectId + - " cannot be set \"className\" or \"properties\" again . the two in conflict is " + - objectModel + " and " + objectModelMap.get(sameObjectId) + " ." - ); - } - objectModelMap.put(sameObjectId, objectModel); - return; - } - propRequireNotNull(objectModel.getClassName(), propNameSup.get()); - - } - - private static void checkAndPutObjectModelHasDifferentId(ObjectModel objectModel, - Map objectModelMap, - String propName) - throws IllegalSchedulingPropertyException { - checkAndPutObjectModelHasDifferentId(objectModel, objectModelMap, () -> propName); - } - - /** - * 检查属性是否为null - * - * @param prop 属性值 - * @param propName 属性名 - * @param reason 原因说明。 - * 默认值为{@code "it's not allow null"},用于{@link #propRequireNotNull(Object, String)}与 - * {@link #propRequireNotNull(Object, Supplier)}方法中。 - * @param 属性值泛型 - * @return 返回传入的属性值,供继续调用 - * @throws IllegalSchedulingPropertyException 如果属性为null,抛出异常 - */ - private static T propRequireNotNull(T prop, Supplier propName, Supplier reason) - throws IllegalSchedulingPropertyException { - if (prop == null) { - throw IllegalSchedulingPropertyException.illegalFieldParameter( - propName.get(), - null, - reason.get() - ); - } - return prop; - } - - private static T propRequireNotNull(T prop, String propName) throws IllegalSchedulingPropertyException { - return propRequireNotNull(prop, () -> propName); - } - - private static T propRequireNotNull(T prop, Supplier propName) throws IllegalSchedulingPropertyException { - return propRequireNotNull(prop, propName, () -> "it's not allow null"); - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java deleted file mode 100644 index f0b5b4b..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawings.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.jd.platform.async.scheduling.drawings; - -/** - * 工厂图纸,可从模型转化而来 - * - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:00 - */ -public interface SchedulingDrawings { - -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java deleted file mode 100644 index 7cf3a4b..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jd.platform.async.scheduling.drawings; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:39 - */ -class SchedulingDrawingsImpl implements SchedulingDrawings{ - protected String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java deleted file mode 100644 index 178f47b..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/drawings/SchedulingDrawingsParser.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.jd.platform.async.scheduling.drawings; - -import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; -import com.jd.platform.async.scheduling.model.SchedulingDrawingsModel; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-上午12:03 - */ -public interface SchedulingDrawingsParser { - /** - * 从模型转化为图纸 - */ - SchedulingDrawings parseDrawings(SchedulingDrawingsModel model) throws IllegalSchedulingPropertyException; - - default SchedulingDrawingsParser getDefaultInstance() { - return DefaultSchedulingDrawingsParser.instance; - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java deleted file mode 100644 index 8a49582..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingException.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.jd.platform.async.scheduling.exception; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:20 - */ -public class IllegalSchedulingException extends Exception{ - public IllegalSchedulingException() { - } - - public IllegalSchedulingException(String message) { - super(message); - } - - public IllegalSchedulingException(String message, Throwable cause) { - super(message, cause); - } - - public IllegalSchedulingException(Throwable cause) { - super(cause); - } - - public IllegalSchedulingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java deleted file mode 100644 index 459c65a..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/exception/IllegalSchedulingPropertyException.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jd.platform.async.scheduling.exception; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/20-下午4:59 - */ -public class IllegalSchedulingPropertyException extends IllegalSchedulingException { - - public IllegalSchedulingPropertyException(String message) { - super(message); - } - - public IllegalSchedulingPropertyException(String message, Throwable cause) { - super(message, cause); - } - - /** - * json反序列化失败 - * - * @param json json - * @param cause 原因 - * @return 返回异常 - */ - public static IllegalSchedulingPropertyException deserializeJsonFailed(String json, - Throwable cause) { - return new IllegalSchedulingPropertyException( - "Json deserialize to model failed , please check properties and see QuickStart.md . the json is : " + json, - cause - ); - } - - /** - * 如果json反序列化有效,但是不符合规范。 - * - * @param illegalFieldName 无效的属性名 - * @param fieldValue 属性的值 - * @param reason 原因 - * @param cause 引发异常,可以为null - * @return 返回异常 - */ - public static IllegalSchedulingPropertyException illegalFieldParameter(String illegalFieldName, - Object fieldValue, - String reason, - Throwable cause) { - return new IllegalSchedulingPropertyException( - "Property" + illegalFieldName + " does not conform to specification. value is : " + fieldValue - + " . because " + reason, cause - ); - } - - public static IllegalSchedulingPropertyException illegalFieldParameter(String illegalFieldName, - Object fieldValue, - String reason) { - return illegalFieldParameter(illegalFieldName, fieldValue, reason, null); - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java deleted file mode 100644 index 516b23a..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/factory/AbstractSchedulingFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jd.platform.async.scheduling.factory; - -import java.util.concurrent.atomic.AtomicLong; - -/** - * 调度工厂。传入图纸生成一组wrapper。 - * - * @author create by TcSnZh on 2021/5/17-上午1:11 - */ -public abstract class AbstractSchedulingFactory { - protected final String factoryName; - - /** - * 无参构造,默认使用 {@code 栈信息<自增long值> } 作为工厂名 - */ - public AbstractSchedulingFactory() { - this(Thread.currentThread().getStackTrace()[2] + "<" + defaultFactoryNameCount.getAndIncrement() + ">"); - } - - /** - * 指定工厂名 - * - * @param factoryName 工厂名 - */ - public AbstractSchedulingFactory(String factoryName) { - this.factoryName = factoryName; - } - - // ========== static ========== - - private static final AtomicLong defaultFactoryNameCount = new AtomicLong(); -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java deleted file mode 100644 index 6e62218..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/DefaultSchedulingJsonModelParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jd.platform.async.scheduling.model; - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.parser.Feature; -import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; - -import static com.alibaba.fastjson.parser.Feature.*; - -/** - * @author create by TcSnZh on 2021/5/17-上午1:22 - */ -class DefaultSchedulingJsonModelParser implements SchedulingJsonModelParser { - // ========== singleton instance ========== - - static final DefaultSchedulingJsonModelParser instance = new DefaultSchedulingJsonModelParser(); - - // ========== public methods ========== - - @Override - public SchedulingDrawingsModel parseToModel(String json) throws IllegalSchedulingPropertyException { - try { - return JSONObject.parseObject(json, SchedulingDrawingsModel.class, DEFAULT_FEATURES); - } catch (Exception e) { - throw IllegalSchedulingPropertyException.deserializeJsonFailed(json, e); - } - } - - static Feature[] DEFAULT_FEATURES = { - AllowComment, AllowUnQuotedFieldNames, AllowSingleQuotes, SafeMode, ErrorOnEnumNotMatch - }; - - // ========== util methods ========== -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java deleted file mode 100644 index ea4783c..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/ObjectModel.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.jd.platform.async.scheduling.model; - -import com.jd.platform.async.callback.ICallback; -import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; -import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; -import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/19-下午7:51 - */ -public class ObjectModel { - protected String constObjectName; - protected String className; - protected Long sameObjectId; - protected Map properties; - - public ObjectModel() { - } - - public String getConstObjectName() { - return constObjectName; - } - - public void setConstObjectName(String constObjectName) { - this.constObjectName = constObjectName; - } - - public String getClassName() { - return className; - } - - public void setClassName(String className) { - this.className = className; - } - - public Long getSameObjectId() { - return sameObjectId; - } - - public void setSameObjectId(Long sameObjectId) { - this.sameObjectId = sameObjectId; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - public static Map getConstObjects() { - return constObjects; - } - - @Override - public String toString() { - return "ObjectModel{" + - "constObjectName='" + constObjectName + '\'' + - ", className='" + className + '\'' + - ", sameObjectId=" + sameObjectId + - ", properties=" + properties + - '}'; - } - - // static constants - - private static final Map constObjects; - - static { - constObjects = new HashMap<>(16); - constObjects.put("NOT_SKIP", SkipStrategy.NOT_SKIP); - constObjects.put("CHECK_ONE_LEVEL", SkipStrategy.CHECK_ONE_LEVEL); - constObjects.put("ALL_DEPENDENCIES_ALL_SUCCESS", DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS); - constObjects.put("ALL_DEPENDENCIES_ANY_SUCCESS", DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS); - constObjects.put("ALL_DEPENDENCIES_NONE_FAILED", DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED); - constObjects.put("SUCCESS_CONTINUE", DependOnUpWrapperStrategy.SUCCESS_CONTINUE); - constObjects.put("SUCCESS_START_INIT_CONTINUE", DependOnUpWrapperStrategy.SUCCESS_START_INIT_CONTINUE); - constObjects.put("PRINT_EXCEPTION_STACK_TRACE", ICallback.PRINT_EXCEPTION_STACK_TRACE); - } - - public static boolean containConstObject(String name) { - return constObjects.containsKey(name); - } - - public static T getConstObject(String name) { - //noinspection unchecked - return (T) constObjects.get(name); - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java deleted file mode 100644 index 6899622..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingDrawingsModel.java +++ /dev/null @@ -1,300 +0,0 @@ -package com.jd.platform.async.scheduling.model; - -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; -import java.util.concurrent.TimeUnit; - -/** - * 工厂图纸模型对象。 - * 具体参数含义,请查阅 QuickStart.md - * - * @author create by TcSnZh on 2021/5/17-上午1:16 - */ -@SuppressWarnings("unused") -public class SchedulingDrawingsModel { - protected String drawingsName; - protected List wrappers; - protected List relations; - protected BeginWorkModel beginWork; - - public static class WrapperModel { - protected String id; - protected ParamModel param; - protected ObjectModel worker; - protected ObjectModel callback; - protected WrapperStrategyModel wrapperStrategy; - protected Boolean allowInterrupt; - protected Boolean enableTimeout; - protected Long timeoutLength; - protected TimeUnit timeUnit; - protected String extendConfig; - - public static class ParamModel { - protected Boolean useObjectModel; - protected Object value; // true - ObjectModel ; false - the json converted to type, such as Map\List\String... - - public Boolean getUseObjectModel() { - return useObjectModel; - } - - public void setUseObjectModel(Boolean useObjectModel) { - this.useObjectModel = useObjectModel; - } - - public Object getValue() { - return value; - } - - public void setValue(Object value) { - this.value = value; - } - } - - public static class WrapperStrategyModel { - protected Map dependOnUpWrapperStrategyMapper; - protected ObjectModel dependenceStrategy; - protected ObjectModel skipStrategy; - - public Map getDependOnUpWrapperStrategyMapper() { - return dependOnUpWrapperStrategyMapper; - } - - public void setDependOnUpWrapperStrategyMapper(Map dependOnUpWrapperStrategyMapper) { - this.dependOnUpWrapperStrategyMapper = dependOnUpWrapperStrategyMapper; - } - - public ObjectModel getDependenceStrategy() { - return dependenceStrategy; - } - - public void setDependenceStrategy(ObjectModel dependenceStrategy) { - this.dependenceStrategy = dependenceStrategy; - } - - public ObjectModel getSkipStrategy() { - return skipStrategy; - } - - public void setSkipStrategy(ObjectModel skipStrategy) { - this.skipStrategy = skipStrategy; - } - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public ParamModel getParam() { - return param; - } - - public void setParam(ParamModel param) { - this.param = param; - } - - public ObjectModel getWorker() { - return worker; - } - - public void setWorker(ObjectModel worker) { - this.worker = worker; - } - - public ObjectModel getCallback() { - return callback; - } - - public void setCallback(ObjectModel callback) { - this.callback = callback; - } - - public WrapperStrategyModel getWrapperStrategy() { - return wrapperStrategy; - } - - public void setWrapperStrategy(WrapperStrategyModel wrapperStrategy) { - this.wrapperStrategy = wrapperStrategy; - } - - public Boolean getAllowInterrupt() { - return allowInterrupt; - } - - public void setAllowInterrupt(Boolean allowInterrupt) { - this.allowInterrupt = allowInterrupt; - } - - public Boolean getEnableTimeout() { - return enableTimeout; - } - - public void setEnableTimeout(Boolean enableTimeout) { - this.enableTimeout = enableTimeout; - } - - public Long getTimeoutLength() { - return timeoutLength; - } - - public void setTimeoutLength(Long timeoutLength) { - this.timeoutLength = timeoutLength; - } - - public TimeUnit getTimeUnit() { - return timeUnit; - } - - public void setTimeUnit(TimeUnit timeUnit) { - this.timeUnit = timeUnit; - } - - public String getExtendConfig() { - return extendConfig; - } - - public void setExtendConfig(String extendConfig) { - this.extendConfig = extendConfig; - } - - @Override - public String toString() { - return "WrapperModel{" + - "id='" + id + '\'' + - ", param=" + param + - ", worker=" + worker + - ", callback=" + callback + - ", wrapperStrategy=" + wrapperStrategy + - ", allowInterrupt=" + allowInterrupt + - ", enableTimeout=" + enableTimeout + - ", timeoutLength=" + timeoutLength + - ", timeUnit=" + timeUnit + - ", extendConfig='" + extendConfig + '\'' + - '}'; - } - } - - public static class RelationModel { - protected Object from; // from和to最多有一个数组,剩下的都是String - protected Object to; - - public Object getFrom() { - return from; - } - - public void setFrom(Object from) { - this.from = from; - } - - public Object getTo() { - return to; - } - - public void setTo(Object to) { - this.to = to; - } - - @Override - public String toString() { - return "RelationModel{" + - "from=" + from + - ", to=" + to + - '}'; - } - } - - public static class BeginWorkModel { - protected Long timeoutLength; - protected TimeUnit timeoutUnit; - protected List wrappers; - protected String executor; - - public Long getTimeoutLength() { - return timeoutLength; - } - - public void setTimeoutLength(Long timeoutLength) { - this.timeoutLength = timeoutLength; - } - - public TimeUnit getTimeoutUnit() { - return timeoutUnit; - } - - public void setTimeoutUnit(TimeUnit timeoutUnit) { - this.timeoutUnit = timeoutUnit; - } - - public List getWrappers() { - return wrappers; - } - - public void setWrappers(List wrappers) { - this.wrappers = wrappers; - } - - public String getExecutor() { - return executor; - } - - public void setExecutor(String executor) { - this.executor = executor; - } - - @Override - public String toString() { - return "BeginWorkModel{" + - "timeoutLength=" + timeoutLength + - ", timeoutUnit=" + timeoutUnit + - ", wrappers=" + wrappers + - ", executor='" + executor + '\'' + - '}'; - } - } - - public String getDrawingsName() { - return drawingsName; - } - - public void setDrawingsName(String drawingsName) { - this.drawingsName = drawingsName; - } - - public List getWrappers() { - return wrappers; - } - - public void setWrappers(List wrappers) { - this.wrappers = wrappers; - } - - public List getRelations() { - return relations; - } - - public void setRelations(List relations) { - this.relations = relations; - } - - public BeginWorkModel getBeginWork() { - return beginWork; - } - - public void setBeginWork(BeginWorkModel beginWork) { - this.beginWork = beginWork; - } - - @Override - public String toString() { - return "SchedulingDrawingsModel{" + - "drawingsName='" + drawingsName + '\'' + - ", wrappers=" + wrappers + - ", relations=" + relations + - ", beginWork=" + beginWork + - '}'; - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java deleted file mode 100644 index f3cf69a..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/model/SchedulingJsonModelParser.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jd.platform.async.scheduling.model; - -import com.jd.platform.async.scheduling.exception.IllegalSchedulingPropertyException; - -/** - * @author create by TcSnZh on 2021/5/17-下午7:22 - */ -public interface SchedulingJsonModelParser { - /** - * 解析json为配置模型对象 - * - * @param json json - * @return 返回图纸对象接口 - */ - SchedulingDrawingsModel parseToModel(String json) throws IllegalSchedulingPropertyException; - - /** - * 默认实现 - */ - static SchedulingJsonModelParser getDefaultInstance() { - return DefaultSchedulingJsonModelParser.instance; - } -} diff --git a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java b/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java deleted file mode 100644 index 62d1675..0000000 --- a/asyncTool-scheduling/src/main/java/com/jd/platform/async/scheduling/util/ReflectUtil.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jd.platform.async.scheduling.util; - -/** - * 反射工具类 - * - * @author tcsnzh[zh.jobs@foxmail.com] - */ -@SuppressWarnings("AlibabaAbstractClassShouldStartWithAbstractNaming") -public abstract class ReflectUtil { - private ReflectUtil() { - } - -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java deleted file mode 100644 index bda04e1..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/FileStringReader.java +++ /dev/null @@ -1,37 +0,0 @@ -package v15.schedulingtest; - -import java.io.*; -import java.util.Objects; - -/** - * @author create by TcSnZh on 2021/5/17-上午11:34 - */ -public class FileStringReader { - public static String readFile(String resourceClasspath) { - InputStream is = null; - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( - is = Objects.requireNonNull( - Thread.currentThread().getContextClassLoader(), "get classLoader is null" - ).getResourceAsStream(resourceClasspath), () -> "get resource " + resourceClasspath + " is null" - ))); - StringWriter stringWriter = new StringWriter(); - char[] buf = new char[4096]; - int len; - while ((len = reader.read(buf)) > 0) { - stringWriter.write(buf, 0, len); - } - return stringWriter.toString(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java deleted file mode 100644 index 8f40ae1..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/Case1.java +++ /dev/null @@ -1,15 +0,0 @@ -package v15.schedulingtest.cases.case1; - -import com.jd.platform.async.scheduling.exception.IllegalSchedulingException; -import com.jd.platform.async.scheduling.model.SchedulingJsonModelParser; -import v15.schedulingtest.FileStringReader; - -/** - * @author create by TcSnZh on 2021/5/17-上午11:49 - */ -class Case1 { - public static void main(String[] args) throws IllegalSchedulingException { - String json = FileStringReader.readFile("v15/case1_1.json"); - System.out.println(SchedulingJsonModelParser.getDefaultInstance().parseToModel(json)); - } -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java deleted file mode 100644 index 71f6e56..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PayTaxes.java +++ /dev/null @@ -1,34 +0,0 @@ -package v15.schedulingtest.cases.case1; - -import com.jd.platform.async.callback.ICallback; -import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.worker.WorkResult; -import com.jd.platform.async.wrapper.WorkerWrapper; - -import java.util.Map; - -/** - * @author create by TcSnZh on 2021/5/17-上午2:04 - */ -class PayTaxes implements IWorker, ICallback { - /** - * 富人(资产>1亿)收30%的税,穷人发1000块。 - */ - @Override - public User action(String selectUserWrapperId, Map> allWrappers) { - User u = (User) allWrappers.get(selectUserWrapperId).getWorkResult().getResult(); - double money; - if ((money = u.getMoney()) > 1.00 * SelectUserByName.HundredMillion) { - u.setMoney(money * 0.7); - } else { - u.setMoney(money + 1000); - } - return u; - } - - @Override - public void result(boolean success, String ignore, WorkResult workResult) { - User u = workResult.getResult(); - (success ? System.out : System.err).printf("User %s has %.2f yuan .\n", u.getName(), u.getMoney()); - } -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java deleted file mode 100644 index c46166c..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/PrintParam.java +++ /dev/null @@ -1,17 +0,0 @@ -package v15.schedulingtest.cases.case1; - -import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.wrapper.WorkerWrapper; - -import java.util.Map; - -/** - * @author create by TcSnZh on 2021/5/17-上午2:31 - */ -class PrintParam implements IWorker { - @Override - public Object action(Object object, Map> allWrappers) { - System.out.println("print param : " + object); - return object; - } -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java deleted file mode 100644 index b796933..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/SelectUserByName.java +++ /dev/null @@ -1,48 +0,0 @@ -package v15.schedulingtest.cases.case1; - -import com.jd.platform.async.callback.ICallback; -import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.worker.WorkResult; -import com.jd.platform.async.wrapper.WorkerWrapper; -import v15.schedulingtest.cases.case1.User; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author create by TcSnZh on 2021/5/17-上午1:33 - */ -class SelectUserByName implements IWorker, ICallback { - public static final long HundredMillion = 100000000; - private static final Map datasource; - - static { - datasource = new ConcurrentHashMap<>(); - User pony = new User("Pony", 50, 612.0 * HundredMillion); - User jackMa = new User("JackMa", 57, 475.0 * HundredMillion); - User me = new User("tcsnzh", 20, 0.01); - datasource.put(pony.getName(), pony); - datasource.put(jackMa.getName(), jackMa); - datasource.put(me.getName(), me); - } - - private static final AtomicInteger queryCount = new AtomicInteger(); - - /** - * 查询用户 - */ - @Override - public User action(String param, Map> allWrappers) { - return datasource.get(param); - } - - @Override - public void result(boolean success, String param, WorkResult workResult) { - (success ? System.out : System.err).println( - "query[" + queryCount.getAndIncrement() + "] " + - "with parameter " + param + - (success ? " succeeded" : " failed") + - ". workResult is " + workResult); - } -} diff --git a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java b/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java deleted file mode 100644 index 9a192ee..0000000 --- a/asyncTool-scheduling/src/test/java/v15/schedulingtest/cases/case1/User.java +++ /dev/null @@ -1,52 +0,0 @@ -package v15.schedulingtest.cases.case1; - -/** - * @author create by TcSnZh on 2021/5/17-上午1:33 - */ -class User { - private String name; - private int age; - private double money; - - public User() { - } - - public User(String name, int age, double money) { - this.name = name; - this.age = age; - this.money = money; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public double getMoney() { - return money; - } - - public void setMoney(double money) { - this.money = money; - } - - @Override - public String toString() { - return "User{" + - "name='" + name + '\'' + - ", age=" + age + - ", money=" + money + - '}'; - } -} diff --git a/asyncTool-scheduling/src/test/resources/v15/case1_1.json b/asyncTool-scheduling/src/test/resources/v15/case1_1.json deleted file mode 100644 index cedf0fb..0000000 --- a/asyncTool-scheduling/src/test/resources/v15/case1_1.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "drawingsName": "case1_1", - "wrappers": [ - { - "id": "first", - "param": { - "useObjectModel": false, - "value": "JackMa" - }, - "worker": { - "sameObjectId": 1, - "className:": "schedulingtest.impl.SelectUserByName" - }, - "callback": { - "sameObjectId": 1 - }, - "wrapperStrategy": { - "dependOnUpWrapperStrategyMapper": null, - "dependenceStrategy": { - "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" - }, - "skipStrategy": { - "constObjectName": "CHECK_ONE_LEVEL" - } - }, - "allowInterrupt": true, - "enableTimeout": true, - "timeoutLength": 50, - "timeoutUnit": "TimeUnit.MILLISECONDS" - }, - { - "id": "second", - "worker": { - "sameObjectId": 2, - "className:": "schedulingtest.impl.SelectUserByName" - }, - "callback": { - "sameObjectId": 2 - } - }, - { - "id": "third", - "__extend": "second" - }, - { - "id": "fourth", - "worker": { - "sameObjectId": 2, - "className:": "schedulingtest.impl.PrintParam" - }, - "wrapperStrategy": { - "dependenceStrategy": { - "constObjectName": "ALL_DEPENDENCIES_ALL_SUCCESS" - } - } - } - ], - "relations": [ - { - "from": "first", - "to": "second" - }, - { - "from": "second", - "to": "fourth" - }, - { - "from": "third", - "to": "fourth" - } - ], - "beginWork": { - "timeoutLength": 100, - "timeoutUnit": "MILLISECONDS", - "wrappers": [ - "first" - ], - "executor": "COMMON_POOL" - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7d4ab74..cdb66dc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.jd.platform asyncTool pom - 1.5.0-SNAPSHOT + 1.5.1-SNAPSHOT @@ -25,6 +25,5 @@ asyncTool-openutil asyncTool-core - asyncTool-scheduling \ No newline at end of file -- Gitee From 46f76f6bf882d3955187c314b5f601a18cf7ddb8 Mon Sep 17 00:00:00 2001 From: TcSnZh <1293969878@qq.com> Date: Thu, 13 May 2021 00:24:44 +0800 Subject: [PATCH 5/5] =?UTF-8?q?v1.5.1-SNAPSHOT=201.rebase=20=202.=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=B8=8D=E8=89=AF=E4=BB=A3=E7=A0=81=203.=E6=9B=B4?= =?UTF-8?q?=E6=96=B0QuickStart.md=20=204.=E5=88=86=E6=A8=A1=E5=9D=97=20=20?= =?UTF-8?q?5.=E4=BD=BF=E7=94=A8=E4=BB=BB=E5=8A=A1=E5=8F=A5=E6=9F=84OnceWor?= =?UTF-8?q?k=20=206.=E5=A2=9E=E5=8A=A0=E4=BB=BB=E5=8A=A1=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 40 +- QuickStart.md | 1478 +++++++++++++++-- README.md | 13 +- asyncTool-core/pom.xml | 27 + .../async/callback/DefaultCallback.java | 8 +- .../async/callback/DefaultGroupCallback.java | 0 .../jd/platform/async/callback/ICallback.java | 20 +- .../async/callback/IGroupCallback.java | 0 .../async/callback/ITimeoutWorker.java | 0 .../jd/platform/async/callback/IWorker.java | 8 +- .../exception/CancelSkippedException.java | 19 + .../async/exception/SkippedException.java | 16 +- .../com/jd/platform/async/executor/Async.java | 178 +- .../async/executor/PollingCenter.java | 87 + .../async/executor/timer/SystemClock.java | 1 - .../jd/platform/async/worker/OnceWork.java | 375 +++++ .../jd/platform/async/worker/ResultState.java | 0 .../jd/platform/async/worker/WorkResult.java | 25 +- .../wrapper/QuickBuildWorkerWrapper.java | 100 ++ .../async/wrapper/StableWorkerWrapper.java | 16 +- .../wrapper/StableWorkerWrapperBuilder.java | 95 +- .../platform/async/wrapper/WorkerWrapper.java | 786 +++++++++ .../async/wrapper/WorkerWrapperBuilder.java | 39 +- .../async/wrapper/WorkerWrapperGroup.java | 212 +++ .../wrapper/strategy/WrapperStrategy.java | 135 ++ .../depend}/DependMustStrategyMapper.java | 4 +- .../depend/DependOnUpWrapperStrategy.java | 12 +- .../DependOnUpWrapperStrategyMapper.java | 15 +- .../strategy/depend}/DependenceAction.java | 13 +- .../strategy/depend}/DependenceStrategy.java | 44 +- .../wrapper/strategy/skip/SkipStrategy.java | 53 + .../test/java/beforev14/depend/DeWorker.java | 2 +- .../test/java/beforev14/depend/DeWorker1.java | 2 +- .../test/java/beforev14/depend/DeWorker2.java | 2 +- .../java/beforev14/depend/LambdaTest.java | 7 +- .../src}/test/java/beforev14/depend/Test.java | 0 .../src}/test/java/beforev14/depend/User.java | 0 .../java/beforev14/dependnew/DeWorker.java | 2 +- .../java/beforev14/dependnew/DeWorker1.java | 2 +- .../java/beforev14/dependnew/DeWorker2.java | 2 +- .../test/java/beforev14/dependnew/Test.java | 0 .../test/java/beforev14/dependnew/User.java | 0 .../beforev14/parallel/ParTimeoutWorker.java | 2 +- .../java/beforev14/parallel/ParWorker.java | 2 +- .../java/beforev14/parallel/ParWorker1.java | 2 +- .../java/beforev14/parallel/ParWorker2.java | 2 +- .../java/beforev14/parallel/ParWorker3.java | 2 +- .../java/beforev14/parallel/ParWorker4.java | 2 +- .../test/java/beforev14/parallel/TestPar.java | 0 .../java/beforev14/seq/SeqTimeoutWorker.java | 2 +- .../test/java/beforev14/seq/SeqWorker.java | 2 +- .../test/java/beforev14/seq/SeqWorker1.java | 2 +- .../test/java/beforev14/seq/SeqWorker2.java | 2 +- .../java/beforev14/seq/TestSequential.java | 0 .../beforev14/seq/TestSequentialTimeout.java | 0 .../src/test/java/v15/cases/Case0.java | 37 + .../src/test/java/v15/cases/Case01.java | 37 + .../src/test/java/v15/cases/Case02.java | 38 + .../src/test/java/v15/cases/Case1.java | 62 + .../src/test/java/v15/cases/Case10.java | 72 + .../src/test/java/v15/cases/Case2.java | 53 + .../src/test/java/v15/cases/Case3.java | 63 + .../src/test/java/v15/cases/Case4.java | 69 + .../src/test/java/v15/cases/Case5.java | 75 + .../src/test/java/v15/cases/Case6.java | 59 + .../src/test/java/v15/cases/Case7.java | 61 + .../src/test/java/v15/cases/Case8.java | 74 + .../src/test/java/v15/cases/Case9.java | 65 + .../src/test/java/v15/wrappertest}/Test.java | 14 +- asyncTool-openutil/pom.xml | 19 + .../com/jd/platform/async/openutil/BiInt.java | 109 ++ .../openutil/collection/AbstractArray2D.java | 74 + .../collection/AbstractDirectedGraph.java | 88 + .../openutil/collection/AbstractStoreArk.java | 22 + .../async/openutil/collection/Array2D.java | 169 ++ .../openutil/collection/CachedStoreArk.java | 60 + .../collection/CommonDirectedGraph.java | 145 ++ .../openutil/collection/CommonStoreArk.java | 159 ++ .../openutil/collection/DirectedGraph.java | 182 ++ .../async/openutil/collection/Graph.java | 106 ++ .../openutil/collection/SparseArray2D.java | 230 +++ .../async/openutil/collection/StoreArk.java | 69 + .../openutil/timer/AbstractWheelTimer.java | 18 + .../openutil/timer/HashedWheelTimer.java | 665 ++++++++ .../async/openutil/timer/Timeout.java | 37 + .../platform/async/openutil/timer/Timer.java | 43 + .../async/openutil/timer/TimerTask.java | 10 + .../test/java/openutiltest/PrintProxy.java | 51 + .../src/test/java/openutiltest/TestGraph.java | 37 + pom.xml | 7 +- .../platform/async/wrapper/WorkerWrapper.java | 720 -------- .../async/wrapper/WrapperEndingInspector.java | 486 ------ .../wrapper/skipstrategy/SkipStrategy.java | 183 -- 93 files changed, 6561 insertions(+), 1765 deletions(-) create mode 100644 asyncTool-core/pom.xml rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/DefaultCallback.java (66%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/ICallback.java (49%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/IGroupCallback.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/ITimeoutWorker.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/callback/IWorker.java (80%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/exception/SkippedException.java (55%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/executor/Async.java (59%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/executor/timer/SystemClock.java (97%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/worker/ResultState.java (100%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/worker/WorkResult.java (70%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java (73%) rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java (84%) create mode 100755 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java rename {src => asyncTool-core/src}/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java (89%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java rename {src/main/java/com/jd/platform/async/wrapper/actionstrategy => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend}/DependMustStrategyMapper.java (95%) rename src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java (85%) rename src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java (75%) rename {src/main/java/com/jd/platform/async/wrapper/actionstrategy => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend}/DependenceAction.java (80%) rename {src/main/java/com/jd/platform/async/wrapper/actionstrategy => asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend}/DependenceStrategy.java (89%) create mode 100644 asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker.java (92%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker1.java (97%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/DeWorker2.java (97%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/LambdaTest.java (95%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/Test.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/depend/User.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker.java (92%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker1.java (94%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/DeWorker2.java (94%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/Test.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/dependnew/User.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParTimeoutWorker.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker1.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker2.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker3.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/ParWorker4.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/parallel/TestPar.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqTimeoutWorker.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker1.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/SeqWorker2.java (98%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/TestSequential.java (100%) rename {src => asyncTool-core/src}/test/java/beforev14/seq/TestSequentialTimeout.java (100%) create mode 100644 asyncTool-core/src/test/java/v15/cases/Case0.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case01.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case02.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case1.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case10.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case2.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case3.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case4.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case5.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case6.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case7.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case8.java create mode 100644 asyncTool-core/src/test/java/v15/cases/Case9.java rename {src/test/java/v15/dependnew => asyncTool-core/src/test/java/v15/wrappertest}/Test.java (96%) create mode 100644 asyncTool-openutil/pom.xml create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java create mode 100644 asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java create mode 100644 asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java create mode 100644 asyncTool-openutil/src/test/java/openutiltest/TestGraph.java delete mode 100755 src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java delete mode 100644 src/main/java/com/jd/platform/async/wrapper/WrapperEndingInspector.java delete mode 100644 src/main/java/com/jd/platform/async/wrapper/skipstrategy/SkipStrategy.java diff --git a/.gitignore b/.gitignore index 66d02f6..991259a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,32 @@ -HELP.md -target/ +**/HELP.md +**/target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/** !**/src/test/** ### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache +**/.apt_generated +**/.classpath +**/.factorypath +**/.project +**/.settings +**/.springBeans +**/.sts4-cache ### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr +**/.idea +**/*.iws +**/*.iml +**/*.ipr ### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ +**/nbproject/private/ +**/nbbuild/ +**/dist/ +**/nbdist/ +**/.nb-gradle/ +**/build/ ### VS Code ### -.vscode/ +**/.vscode/ diff --git a/QuickStart.md b/QuickStart.md index f191d47..ec1e1db 100644 --- a/QuickStart.md +++ b/QuickStart.md @@ -1,40 +1,84 @@ 如果只是需要用这个框架,请往下看即可。如果需要深入了解这个框架是如何一步一步实现的,从接到需求,到每一步的思考,每个类为什么这么设计,为什么有这些方法,也就是如何从0到1开发出这个框架,作者在[csdn开了专栏](https://blog.csdn.net/tianyaleixiaowu/category_9637010.html)专门讲中间件如何从0开发,包括并不限于这个小框架。京东内部同事可在cf上搜索erp也能看到。 +# 安装教程 + +代码不多,直接拷贝包过去即可。 + +#### 旧稳定版本v1.4 + 京东同事通过引用如下maven来使用。 -``` - - com.jd.platform - asyncTool - 1.4.1-SNAPSHOT - +```xml + + com.jd.platform + asyncTool + 1.4.1-SNAPSHOT + ``` 外网请使用jitpack.io上打的包 先添加repositories节点 -``` - - - jitpack.io - https://jitpack.io - - +```xml + + + jitpack.io + https://jitpack.io + + ``` 然后添加如下maven依赖 +```xml + + com.gitee.jd-platform-opensource + asyncTool + V1.4-SNAPSHOT + ``` - - com.gitee.jd-platform-opensource - asyncTool - V1.4-SNAPSHOT - + +#### 最新版本v1.5(不稳定) + + + +从gitee上下载仓库到本地,切换到`dev`分支,然后maven安装到本地仓库。 + +```bash +git clone https://gitee.com/jd-platform-opensource/asyncTool.git +cd ./asyncTool +git checkout dev +mvn install ``` +在项目中引入依赖。 +```xml + + + asyncTool-core + com.jd.platform + 1.5.1-SNAPSHOT + +``` +# 任务编排 -#### 基本组件 -worker: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。 +> `asyncTool-core`核心模块提供了核心功能——任务编排 +> +> 以下文档基于版本: +> +> ```xml +> +> +> com.jd.platform +> asyncTool-core +> 1.5.1-SNAPSHOT +> +> +> ``` + +### 基本组件 + +`IWorker`: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。 T,V两个泛型,分别是入参和出参类型。 @@ -42,263 +86,1369 @@ T,V两个泛型,分别是入参和出参类型。 多个不同的worker之间,没有关联,分别可以有不同的入参、出参类型。 -``` +```java /** * 每个最小执行单元需要实现该接口 + * * @author wuweifeng wrote on 2019-11-19. */ +@FunctionalInterface public interface IWorker { /** * 在这里做耗时操作,如rpc请求、IO等 * - * @param object - * object + * @param object object + * @param allWrappers 任务包装 */ - V dependAction(T object, Map allWrappers); + V action(T object, Map allWrappers); /** - * 超时、异常时,返回的默认值 + * 超时、异常、跳过时,返回的默认值 + * * @return 默认值 */ - V defaultValue(); + default V defaultValue() { + return null; + } } ``` +`ICallback`:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。 -callBack:对每个worker的回调。worker执行完毕后,会回调该接口,带着执行成功、失败、原始入参、和详细的结果。 - -``` +```java /** * 每个执行单元执行完毕后,会回调该接口

* 需要监听执行结果的,实现该接口即可 + * * @author wuweifeng wrote on 2019-11-19. */ +@FunctionalInterface public interface ICallback { - void begin(); + /** + * 任务开始的监听 + */ + default void begin() { + + } /** * 耗时操作执行完毕后,就给value注入值 - * + *

+ * 只要Wrapper被调用后成功或失败/超时,该方法都会被执行。 */ void result(boolean success, T param, WorkResult workResult); } - ``` wrapper:组合了worker和callback,是一个 **最小的调度单元** 。通过编排wrapper之间的关系,达到组合各个worker顺序的目的。 wrapper的泛型和worker的一样,决定了入参和结果的类型。 +```java +// 创建一个WorkerWrapper +WorkerWrapper w0 = WorkerWrapper.builder() + .id("0") + .param("000") + .worker((param, allWrappers) -> "hello : " + param) + .build(); ``` - WorkerWrapper workerWrapper = new WorkerWrapper<>(w, "0", w); - WorkerWrapper workerWrapper1 = new WorkerWrapper<>(w1, "1", w1); - WorkerWrapper workerWrapper2 = new WorkerWrapper<>(w2, "2", w2); - WorkerWrapper workerWrapper3 = new WorkerWrapper<>(w3, "3", w3); +通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。 + +### 如何构造WorkerWrapper? + +##### 推荐Builder模式 + +如果刚开始使用这个框架,则推荐使用如下方式进行构造: + +```java +WorkerWrapper.builder() + .id() + // 其他属性略。 + // 请在《简单示例》与《设置WorkerWrapper属性》中慢慢感受详细内容。 + // 因为这里地方小,写不下。 +``` + +##### 复杂的快速构造 + +> 不推荐新手使用。 +> +> 不推荐在业务中使用,使用Builder模式代码更加简洁,且会检查参数,不必节省这些性能。 +> +> 该对象的构造方法不会检查属性。 + +在对WorkerWrapper属性有充足了解后,可使用“直接设置属性 + 关系图”的方式快速构造wrapper。 + +建议在扩展功能的时候使用该构造器,以提高效率。但是请记得检查参数。 + +以下为示例: + +```java +class Case9 { + public static void main(String[] args) throws ExecutionException, InterruptedException { + DirectedGraph, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>()); + QuickBuildWorkerWrapper w1 = new QuickBuildWorkerWrapper<>("id1", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 1"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + QuickBuildWorkerWrapper w2 = new QuickBuildWorkerWrapper<>("id2", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 2"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + graph.addNode(w1, w2); + graph.putRelation(w1, new Object(), w2); + +// System.out.println(graph); + + Async.work(200, w1).awaitFinish(); + + System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); + /* 输出: + I am IWorker 1 + I am IWorker 2 + Begin work end . + w1 : 省略 + w2 : 省略 + */ + } +} ``` -如 +### 简单示例 -![输入图片说明](https://images.gitee.com/uploads/images/2019/1225/132251_b7cfac23_303698.png "屏幕截图.png") - - 0执行完,同时1和2, 1\2都完成后3。3会等待2完成 - -此时,你可以定义一个 **worker** +1. 3个任务并行 + +![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140256_8c015621_303698.png "屏幕截图.png") + +```java +class Case0 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").build(); + WorkerWrapper c = builder("C").build(); + try { + Async.work(100, a, b, c).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + */ + } +} +``` + + +2. 1个执行完毕后,开启另外两个,另外两个执行完毕后,开始第4个 + +![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140405_93800bc7_303698.png "屏幕截图.png") +```java +class Case01 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").depends(a).build(); + WorkerWrapper c = builder("C").depends(a).build(); + WorkerWrapper f = builder("F").depends(b, c).build(); + try { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=C) is working + wrapper(id=B) is working + wrapper(id=F) is working + */ + } +} ``` -/** - * @author wuweifeng wrote on 2019-11-20. - */ -public class ParWorker1 implements IWorker, ICallback { - @Override - public String dependAction(String object) { +如果觉得`.depneds()`方法的排序您不喜欢,也可以用`.nextOf()`这种方式: + +```java +class Case02 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper f = builder("F").build(); + WorkerWrapper a = builder("A") + .nextOf(builder("B").nextOf(f).build()) + .nextOf(builder("C").nextOf(f).build()) + .build(); try { - Thread.sleep(1000); + Async.work(100, a).awaitFinish(); } catch (InterruptedException e) { e.printStackTrace(); } - return "result = " + SystemClock.now() + "---param = " + object + " from 1"; + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + wrapper(id=F) is working + */ } +} +``` + + - @Override - public String defaultValue() { - return "worker1--default"; +3. 复杂点的 + +![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140445_8d52e4d6_303698.png "屏幕截图.png") + +```java +class Case1 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); } - @Override - public void begin() { - //System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis()); + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper d; + builder("H") + .depends( + builder("F") + .depends(builder("B").depends(a).build()) + .depends(builder("C").depends(a).build()) + .build(), + builder("G") + .depends(builder("E") + .depends(d = builder("D").build()) + .build()) + .build() + ) + .build(); + try { + Async.work(1000, a, d).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=D) is working + wrapper(id=A) is working + wrapper(id=E) is working + wrapper(id=B) is working + wrapper(id=C) is working + wrapper(id=G) is working + wrapper(id=F) is working + wrapper(id=H) is working + */ } +} +``` + +4. 依赖别的worker执行结果作为入参 + +可以从action的参数中根据wrapper的id获取任意一个执行单元的执行结果。 + +但请注意执行顺序,如果尚未执行,则在调用`WorkerResult.getResult()`会得到null! + +```java +class Case2 { + static class AddWork implements IWorker { + private final String id1; + private final String id2; + + public AddWork(String id1, String id2) { + this.id1 = id1; + this.id2 = id2; + } + + public AddWork() { + this(null, null); + } - @Override - public void result(boolean success, String param, WorkResult workResult) { - if (success) { - System.out.println("callback worker1 success--" + SystemClock.now() + "----" + workResult.getResult() - + "-threadName:" +Thread.currentThread().getName()); - } else { - System.err.println("callback worker1 failure--" + SystemClock.now() + "----" + workResult.getResult() - + "-threadName:" +Thread.currentThread().getName()); + @Override + public Integer action(Integer param, Map> allWrappers) { + // 传入的参数 + if (param != null) { + return param; + } + // 将两个id所对应的wrapper的结果取出,相加并返回 + Integer i1 = (Integer) allWrappers.get(id1).getWorkResult().getResult(); + Integer i2 = (Integer) allWrappers.get(id2).getWorkResult().getResult(); + return i1 + i2; } } + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper wrapper100 = WorkerWrapper.builder() + .id("id:100").worker(new AddWork()).param(100).build(); + WorkerWrapper wrapper200 = WorkerWrapper.builder() + .id("id:200").worker(new AddWork()).param(200).build(); + WorkerWrapper add = WorkerWrapper.builder().id("id:add") + .worker(new AddWork("id:100", "id:200")).depends(wrapper100, wrapper200).build(); + Async.work(20,wrapper100,wrapper200).awaitFinish(); + System.out.println(add.getWorkResult()); + // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} + } } +``` + +5. 其他的详见test包下的测试类,支持各种形式的组合、编排。 + +### 使用自定义线程池 +`Async`工具类有多个方法可以使用自定义线程池 + +```java +public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers); + +public static OnceWork work(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper); + +public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers, + String workId); ``` -通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。 +另外,如果没有指定线程池,默认会使用`COMMON_POOL`,您可以调用这些方法获得/关闭此线程池: -#### 安装教程 +> 此线程池将会在第一次使用时懒加载。 -代码不多,直接拷贝包过去即可。 +```java +/** + * 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。 + */ +public static ThreadPoolExecutor getCommonPool(); -#### 使用说明 +/** + * @param now 是否立即关闭 + * @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。 + */ +public static synchronized boolean shutDownCommonPool(boolean now); +``` -1. 3个任务并行 +以下是一个使用自定义线程池的简单代码示例: -![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140256_8c015621_303698.png "屏幕截图.png") +```java +Async.work(1000, Executors.newFixedThreadPool(2),a).awaitFinish(); +``` + +## WorkerWrapper基本属性 + +### 执行流程 + +WorkerWrapper会在这些情况被运行: + +* 在`Async.beginWork`中传入wrapper +* 上游wrapper完成后被调用 +开始运行时,执行逻辑如下图所示: + +![wrapper执行流程](https://gitee.com/tcsn_site/picture/raw/master/wrapper-process.png) + +> processOn流程图文件放在同仓库。 + +### 属性 + +#### id + +`WorkerWrapper`的id属性非常重要。 + +可在builder的该属性设置id,如果不设置,默认使用UUID。 + +```java +public interface WorkerWrapperBuilder { + /** + * 设置唯一id。 + * 如果不设置,{@link StableWorkerWrapperBuilder}会使用UUID + */ + WorkerWrapperBuilder id(String id); + + // 略 +} ``` - ParWorker w = new ParWorker(); - ParWorker1 w1 = new ParWorker1(); - ParWorker2 w2 = new ParWorker2(); - WorkerWrapper workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .build(); +例如如果你需要在`IWorker`中调用上游wrapper,则可以根据id来获取到。 + +> 该map的键即为`WorkerWrapper`的id。 + +```java +V action(T object, Map> allWrappers); +``` + +请程序员确保在一次任务执行的一组wrapper中,id不会重复。在执行过程中不会进行检查。 + +#### 其他省略 + +> 其他属性都写在源码注释中,可下载源码慢慢查看。 + +## `OnceWork`任务句柄详解 + +`Async.work(...)`方法的返回值为`OnceWork`句柄。 + +> 源码里有大量的注释,直接看源码效率更高 + +#### 需要同步等待完成 - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") +`OnceWork`被返回后,任务并没有同步完成,而是还在运行。 + +如果我们需要同步完成,则调用`awaitFinish`方法即可同步等待。 + +#### 取消任务 + +调用`pleaseCancel`方法可取消任务,以下为示例: + +```java +class Case10 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })) + .allowInterrupt(true); + } + + /** + * A(10ms) ==> B(10ms) ==> C(10ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final WorkerWrapper c; + final WorkerWrapper b; + final WorkerWrapper a = builder("A", 10) + .nextOf(b = builder("B", 10) + .nextOf(c = builder("C", 10).build()) + .build()) .build(); + final OnceWork onceWork = Async.work(40, a); + Thread.sleep(25); + onceWork.pleaseCancelAndAwaitFinish(); + System.out.println("任务b信息 " + b); + System.out.println("任务c信息 " + c); + System.out.println("OnceWork信息 " + onceWork); + /* + 可以看到C的state为SKIP,workResult.ex为CancelSkippedException,即被取消了。 + 不过有时程序运行慢,导致B被取消了,那么C就不会执行,其状态就为INIT了。 + */ + } +} +``` + +## 设置WorkerWrapper属性 + +### 设置依赖策略 - WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker(w) - .callback(w) - .param("0") +#### 快速上手 + +`WorkerWrapperBuilder`提供了这些方法来设置依赖策略: + +```java +public interface WorkerWrapperBuilder { + // 略 + + // 各种depends方法都是简便设置依赖的。 + default WorkerWrapperBuilder depends(/* 略 */); + + // 切换到SetDepend模式 + SetDepend setDepend(); + + interface SetDepend { + // 其中各种方法都是用来设置策略的,具体点开源码看注释就行。设置完end()回到Builder模式。 + } + + // 略 +} +``` + +如果没有具体设置策略的话,默认使用`DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS`。具体效果可到《策略器组件默认实现》这一章查看。 + +例如: + +> A之后是B1、B2、B3、B4、B5 +> +> 其中B1与B2全部成功后才能执行C1, +> +> B3、B4、B5任意一个成功后就能执行C2。 + +以下为代码实现: + +```java +class Case3 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b1 = builder("B1").depends(a).build(); + WorkerWrapper b2 = builder("B2").depends(a).build(); + WorkerWrapper b3 = builder("B3").depends(a).build(); + WorkerWrapper b4 = builder("B4").depends(a).build(); + WorkerWrapper b5 = builder("B5").depends(a).build(); + WorkerWrapper c1 = builder("C1") + .depends(DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS, b1, b2) + .build(); + WorkerWrapper c2 = builder("C2") + .depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS, b3, b4, b5) .build(); + // 这里用线程数较少的线程池做示例,对于ALL_DEPENDENCIES_ANY_SUCCESS“仅需一个”的效果会好一点 + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, a).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B3) is working + wrapper(id=B1) is working + wrapper(id=B2) is working + wrapper(id=C2) is working + wrapper(id=C1) is working + wrapper(id=B4) is working + // 我们看到B5被跳过了,没有执行callback + */ + } +} +``` + +##### 策略器组件 - long now = SystemClock.now(); - System.out.println("begin-" + now); +wrapper每次被上游wrapper所调用时,若是还没有结束且不应跳过,都会去根据自己的上游wrapper的状态,来决定自己这次该做什么。 - Async.beginWork(1500, workerWrapper, workerWrapper1, workerWrapper2); -// Async.beginWork(800, workerWrapper, workerWrapper1, workerWrapper2); -// Async.beginWork(1000, workerWrapper, workerWrapper1, workerWrapper2); +`DependenceStrategy`这个函数式接口就是用来判断“这次该做什么的”。 - System.out.println("end-" + SystemClock.now()); - System.err.println("cost-" + (SystemClock.now() - now)); - System.out.println(Async.getThreadCount()); +```java +package com.jd.platform.async.wrapper.dependaction; - System.out.println(workerWrapper.getWorkResult()); - Async.shutDown(); - +//... + +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; + +@FunctionalInterface +public interface DependenceStrategy { + /** + * 核心判断策略 + * + * @param dependWrappers thisWrapper.dependWrappers的属性值。 + * @param thisWrapper thisWrapper,即为“被催促”的WorkerWrapper + * @param fromWrapper 调用来源Wrapper。 + *

+ * 该参数不会为null。 + * 因为在{@link WorkerWrapper#work(ExecutorService, long, Map, WrapperEndingInspector)}方法中传入的的第一批无依赖的Wrapper, + * 不会被该策略器所判断,而是不论如何直接执行。 + *

+ * @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty} + */ + DependenceAction.WithProperty judgeAction(Set> dependWrappers, + WorkerWrapper thisWrapper, + WorkerWrapper fromWrapper); + + // consts 略 + + // methods 略 +} ``` +其返回值`DependenceAction.WithProperty`是枚举`DependenceAction`的一个内部实体类,作用是让返回的枚举值可以多带几个参数。 -2. 1个执行完毕后,开启另外两个,另外两个执行完毕后,开始第4个 +```java +public enum DependenceAction { + START_WORK, + TAKE_REST, + FAST_FAIL, + JUDGE_BY_AFTER; + + // methods ... + + public class WithProperty {/* ... */} +} +``` -![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140405_93800bc7_303698.png "屏幕截图.png") +| 枚举值 | 含义 | +| ---------------- | ------------------------------------------------------------ | +| `START_WORK` | 开始工作。WorkerWrapper会执行工作方法。 | +| `TAKE_REST` | 还没轮到,休息一下。WorkerWrapper中的调用栈会返回,以等待其他上游wrapper调用它,或是会一生无缘被调用。 | +| `FAST_FAIL` | 立即失败。WorkerWrapper会去执行快速失败的方法。 | +| `JUDGE_BY_AFTER` | 交给下层`{@link DependenceStrategy}`进行判断。 由于`{@link DependenceStrategy#thenJudge(DependenceStrategy)}`的责任链设计模式,该返回值的意义就是调用责任链上下一个策略。 | + +> 如果wrapper被跳过,ResultState将为`DEFAULT`。 +> +> + +##### 策略器组件默认实现 + +* `DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS`,该值为默认值,若builder未设置则默认使用这个。 + 1. 被依赖的所有Wrapper都必须成功才能开始工作。 + 2. 如果其中任一Wrapper还没有执行且不存在失败,则休息。 + 3. 如果其中任一Wrapper失败则立即失败。*(跳过不算失败)* +* `DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS` + 1. 被依赖的Wrapper中任意一个成功了就可以开始工作。 + 2. 如果其中所有Wrapper还没有执行,则休息。 + 3. 如果其中一个Wrapper失败且不存在成功则立即失败。*(跳过不算失败)* +* `DependenceStrategy.ALL_DEPENDENCIES_NONE_FAILED` + * 如果被依赖的工作中任一失败,则立即失败。*(跳过不算失败)* + * 否则就开始工作(不论之前的工作有没有开始)。 +* `DependenceStrategy.theseWrapperAllSuccess(Set>)` + * 该方法传入一个`Set`指定wrapper,只有当指定的这些Wrapper都成功时,才会开始工作。任一失败会快速失败。任一还没有执行且不存在失败,则休息。 +* 不建议使用:~~`DependenceStrategy.IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY`~~ + * 此值用于适配v1.4及之前的must开关模式,当`wrapperStrategy`的`dependMustStrategyMapper`的`mustDependSet`不为空时,则休息(因为能判断到这个责任链说明set中存在不满足的值)。为空时,则任一成功则执行。 + +##### `WorkerWrapper`的策略器责任链 + +`WorkerWrapper`在判断时,并不是只使用一个策略进行判断的,而是在`WrapperStrategy`进行了最多三层的判断: + +```java +public interface WrapperStrategy extends DependenceStrategy, SkipStrategy { + // ========== 这三个策略器用于链式判断是否要开始工作 ========== + + // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy + + /** + * 设置对特殊Wrapper专用的依赖响应策略。 + * + * @return 该值允许为null + */ + DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper(); + + /** + * 对必须完成的(must的)Wrapper的依赖响应策略。 + * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 + * + * @return 该值允许为null + */ + DependMustStrategyMapper getDependMustStrategyMapper(); + /** + * 底层全局策略。 + * + * @return 该值不允许为null + */ + DependenceStrategy getDependenceStrategy(); + + // ========== 这是跳过策略 ========== + + /** + * 跳过策略 + * + * @return 不允许为null + */ + SkipStrategy getSkipStrategy(); + + // 其他属性略,自行查看源码即可 +} ``` - ParWorker w = new ParWorker(); - ParWorker1 w1 = new ParWorker1(); - ParWorker2 w2 = new ParWorker2(); - ParWorker3 w3 = new ParWorker3(); - WorkerWrapper workerWrapper3 = new WorkerWrapper.Builder() - .worker(w3) - .callback(w3) - .param("3") - .build(); +正如注释所言,三个策略器将依次调用`judgeAction(Set,WorkerWrapper,WorkerWrapper)`方法进行判断,每次判断会返回`DependenceAction.WithProperty`类型。 - WorkerWrapper workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .next(workerWrapper3) - .build(); +前两个策略器的返回值若不为枚举`JUDGE_BY_AFTER`的内部类时,整个三层责任链将返回此返回值;若为`JUDGE_BY_AFTER`,则交给下个策略器进行判断。该方法具体由以下方法实现: - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") - .next(workerWrapper3) +```java +public interface DependenceStrategy { + // 略 + + /** + * 如果本策略器的judge方法返回了JUDGE_BY_AFTER,则交给下一个策略器来判断。 + * + * @param after 下层策略器 + * @return 返回一个“封装的多层策略器” + */ + default DependenceStrategy thenJudge(DependenceStrategy after) { + DependenceStrategy that = this; + return new DependenceStrategy() { + @Override + public DependenceAction.WithProperty judgeAction( + Set> dependWrappers, + WorkerWrapper thisWrapper, + WorkerWrapper fromWrapper + ) { + DependenceAction.WithProperty judge = that.judgeAction(dependWrappers, thisWrapper, fromWrapper); + if (judge.getDependenceAction() == DependenceAction.JUDGE_BY_AFTER) { + return after.judgeAction(dependWrappers, thisWrapper, fromWrapper); + } + return judge; + } + + @Override + public String toString() { + return that + " ----> " + after; + } + }; + } + + // 略 +} +``` + + + +#### 自定义依赖策略 + +##### 自定义全局策略 + +以下是一个自定义依赖策略的示例。 + +效果是,在B1~B10共10个wrapper,只需3个wrapper成功,即可执行C + +```java +class Case4 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A").build(); + WorkerWrapper c = builder("C") + .setDepend().strategy(new DependenceStrategy() { + @Override + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return dependWrappers.stream() + .filter(workerWrapper -> workerWrapper.getWorkResult().getResultState() == ResultState.SUCCESS) + .count() > 3 ? + DependenceAction.START_WORK.emptyProperty() + : DependenceAction.TAKE_REST.emptyProperty(); + } + }).end() .build(); + for (int i = 1; i < 10; i++) { + builder("B" + i).depends(a).nextOf(c).build(); + } + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, a).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B2) is working + wrapper(id=B1) is working + wrapper(id=B4) is working + wrapper(id=B3) is working + wrapper(id=B5) is working + wrapper(id=C) is working + 由于B1-B10是并行的,所以正好仅有3个wrapper成功,在多线程环境中是比较难遇到的。 + */ + } +} +``` + +##### ~~设置一组必须完成的wrapper(不推荐使用)~~ + +使用以下两个方法,指定的上游wrapper必须全部执行成功,本Wrapper才能执行。 + +```java +public interface WorkerWrapperBuilder { + // 略 + + SetDepend setDepend(); + + interface SetDepend { + // 略 + + /** + * 设置必须要执行成功的Wrapper,当所有被该方法设为的上游Wrapper执行成功时,本Wrapper才能执行 + */ + SetDepend mustRequireWrapper(WorkerWrapper wrapper); + + default SetDepend mustRequireWrapper(WorkerWrapper... wrappers){ + /*...*/} + + // 略 + } + + // 略 +} +``` + +以下是一个不推荐使用的示例: + +```java +class Case5 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } - WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker(w) - .callback(w) - .param("0") - .next(workerWrapper1, workerWrapper2) + @Deprecated + public static void main(String[] args) throws ExecutionException, InterruptedException { + + WorkerWrapper a1 = builder("A1").build(); + WorkerWrapper a2 = builder("A2").build(); + WorkerWrapper a3 = builder("A3").build(); + WorkerWrapper a4 = builder("A4").build(); + WorkerWrapper a5 = builder("A5").build(); + WorkerWrapper a6 = builder("A6").build(); + WorkerWrapper a7 = builder("A7").build(); + WorkerWrapper a8 = builder("A8").build(); + WorkerWrapper a9 = builder("A9").build(); + WorkerWrapper a10 = builder("A10").build(); + builder("B") + .setDepend() + // 必须a3、a4成功才能执行 + .mustRequireWrapper(a3, a4) + // 如果a3、a4没有成功,则休息 + .strategy((dependWrappers, thisWrapper, fromWrapper) -> DependenceAction.TAKE_REST.emptyProperty()) + .wrapper(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + .end() .build(); + WorkerWrapper start = builder("start").nextOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).build(); + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, start).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A1) is working + wrapper(id=A2) is working + wrapper(id=A4) is working + wrapper(id=A3) is working + wrapper(id=A5) is working + wrapper(id=B) is working + wrapper(id=A6) is working + 我们可以看到,A3、A4执行后,B也执行了,之后的wrapper被跳过了 + (这里之所以a5、a6还在执行,只是因为他两正好在WORKING,所以没发现后面的B已经可以跳过了) + */ + } +} +``` + +`.mustRequireWrapper(a3, a4)`方法设置的策略,优先级高于`.strategy()`设置的底层策略。 +##### 对单个wrapper设置“上克下”策略 - long now = SystemClock.now(); - System.out.println("begin-" + now); +我们面临着这种问题: - Async.beginWork(3100, workerWrapper); -// Async.beginWork(2100, workerWrapper); +> 一般来说,都是下游wrapper去根据上游wrapper的状态进行策略判断,然后给出响应。 - System.out.println("end-" + SystemClock.now()); - System.err.println("cost-" + (SystemClock.now() - now)); +那么,能不能让上游wrapper根据自己的状态独自决定下游wrapper响应呢? - System.out.println(Async.getThreadCount()); - Async.shutDown(); +可以。`DependOnUpWrapperStrategy`函数式接口 与 `DependOnUpWrapperStrategyMapper`这两个类即可完成这个功能。 + +```java +/** + * 由上游wrapper决定本wrapper行为的单参数策略。 + * + * @author create by TcSnZh on 2021/5/1-下午11:16 + */ +@FunctionalInterface +public interface DependOnUpWrapperStrategy { + /** + * 仅使用一个参数(即调用自身的上游wrapper)的判断方法 + * + * @param fromWrapper 调用本Wrapper的上游Wrapper + * @return 返回 {@link DependenceAction.WithProperty} + */ + DependenceAction.WithProperty judge(WorkerWrapper fromWrapper); + + // ========== 送几个供链式调用的默认值 ========== + + /** + * 成功时,交给下一个策略器判断。 + * 未运行时,休息。 + * 失败时,失败。 + */ + DependOnUpWrapperStrategy SUCCESS_CONTINUE = /*略*/ ; + /** + * 成功时,开始工作。 + * 未运行时,交给下一个策略器判断。 + * 失败时,失败。 + */ + DependOnUpWrapperStrategy SUCCESS_START_INIT_CONTINUE = /*略*/ ; +} ``` -如果觉得这样不符合左右的顺序,也可以用这种方式: +在`DependOnUpWrapperStrategyMapper`的`mapper`属性中,每个`WorkerWrapper`对应了一个`DependOnUpWrapperStrategy`,实现了让wrapper对不同的上游做出不同的响应策略。 +```java +public class DependOnUpWrapperStrategyMapper implements DependenceStrategy { + private final Map, DependOnUpWrapperStrategy> mapper = new ConcurrentHashMap<>(4); + // 以下略 +} ``` - WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker(w) - .callback(w) - .param("0") - .build(); - WorkerWrapper workerWrapper3 = new WorkerWrapper.Builder() - .worker(w3) - .callback(w3) - .param("3") - .build(); +在《`WorkerWrapper`的三层策略器责任链》这一章中,我们可以看到,第一层策略器就是此`DependOnUpWrapperStrategyMapper`。 - WorkerWrapper workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .depend(workerWrapper) - .next(workerWrapper3) - .build(); +###### 简单使用与示例 + +我们在`SetDepend setDepend();`模式时,可以使用如下方法进行设置 + +```java +/** + * 对单个Wrapper设置特殊策略。 + * + * @param wrapper 需要设置特殊策略的Wrapper。 + * @param strategy 特殊策略。 + */ +SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") - .depend(workerWrapper) - .next(workerWrapper3) +default SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper... wrappers); +``` + +也可以在 `SetNext setNext();`模式时进行设置。 + +```java +/** + * 调用该方法将会让传入的此下游workerWrappers对本Wrapper进行特殊策略判断, + * + * @param strategy 对本Wrapper的特殊策略。 + * @param wrapper 依赖本Wrapper的下游Wrapper。 + * @return 返回Builder自身。 + */ +SetNext specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); +``` + +以下为示例: + +```java +class Case6 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper b = builder("B") + // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 + .depends((dependWrappers, thisWrapper, fromWrapper) -> + DependenceAction.FAST_FAIL + .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) + ) + .callback(ICallback.PRINT_EXCEPTION_STACK_TRACE) + .build(); + WorkerWrapper a = builder("A") + .setNext() + // a将会使b直接开始工作 + // 若是去掉这行代码,则b会失败 + .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) + .wrapper(b) + .end().build(); + Async.work(1000, a).awaitFinish(); + System.out.println(a.getWorkResult()); + System.out.println(b.getWorkResult()); + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + WorkResult{result=null, resultState=SUCCESS, ex=null} + WorkResult{result=null, resultState=SUCCESS, ex=null} + */ + } +} +``` + +###### 提供常量 + +* `DependWrapperActionStrategy.SUCCESS_CONTINUE` + * 成功时,交给下一个策略器判断。未运行时,休息。失败时,失败。 +* `DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE` + * 成功时,开始工作。未运行时,交给下一个策略器判断。失败时,失败。 + +### 设置跳过策略 + +当wrapper发现下游wrapper居然已经被执行了,那是不是可以跳过自身呢? + + of course,当wrapper被跳过时,其`getWorkResult`返回的值通常是: + +```json +{ + result: null, + // 注意:如果wrapper被跳过,ResultState将为DEFAULT + resultState: "ResultState.DEFAULT", + ex: "com.jd.platform.async.exception.SkippedException" +} +``` + +下面是跳过策略的设置接口: + +```java +@FunctionalInterface +public interface SkipStrategy { + /** + * 跳过策略函数。返回true将会使WorkerWrapper跳过执行。 + * + * @param nextWrappers 下游WrapperSet + * @param thisWrapper 本WorkerWrapper + * @param fromWrapper 呼叫本Wrapper的上游Wrapper + * @return 返回true将会使WorkerWrapper跳过执行。 + */ + boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper); + + // consts 略 + +} +``` + +有以下几个默认值: + +* `SkipStrategy.NOT_SKIP` + * 不进行跳过检查,也不打算跳过 +* `SkipStrategy.CHECK_ONE_LEVEL`,该值为默认值,若builder未设置则默认使用这个。 + * 仅检查深度为1的下游wrapper。如果其全部不在初始化状态,则自己会被跳过。 + +以下是一个示例: + +```java +/** + * @author create by TcSnZh on 2021/5/9-下午4:12 + */ +class Case7 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }); + } + + /** + * A ==> B(10ms) ==> C ==> D (D可在E、C任意一个完成后执行) + * . \====> E(5ms) ====/ + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper d = builder("D").depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS).build(); + WorkerWrapper a = builder("A") + .nextOf(builder("B", 10) + .nextOf(builder("C") + .nextOf(d) + // 这里我们没有设置C的跳过策略,因为默认使用CHECK_ONE_LEVEL,可将下行代码注释去掉,则C会执行 +// .setSkipStrategy(SkipStrategy.NOT_SKIP) + .build()) + .build(), + builder("E", 5).nextOf(d).build() + ).build(); + Async.work(1000, a).awaitFinish(); + /* 输出: + wrapper(id=A) is working + wrapper(id=E) is working + wrapper(id=B) is working + wrapper(id=D) is working + */ + } +} +``` + +### 设置超时 + +可以在`Async.beginWork(/* ... */)`中传入总超时时间,也可以对单个wrapper设置超时时间。 + +#### 总任务时间超时 + +在`Async.beginWork`方法中可以指定总超时时间。 + +#### 单wrapper任务时间超时 + +可在Builder中设置超时选项,以下是几个关键方法: + +```java +public interface WorkerWrapperBuilder { + // 略 + + /** + * 设置超时时间的具体属性 + */ + SetTimeOut setTimeOut(); + + interface SetTimeOut { + /** + * 是否启动超时判断。 + *

+ * 默认为true + * + * @param enableElseDisable 是则true + */ + SetTimeOut enableTimeOut(boolean enableElseDisable); + + /** + * 设置单个WorkerWrapper的超时时间。若不设置则不进行超时判断 + * + * @param time 时间数值 + * @param unit 时间单位 + */ + SetTimeOut setTime(long time, TimeUnit unit); + + WorkerWrapperBuilder end(); + } + + /** + * 便携式设置单个WorkerWrapper的超时时间。若不设置则不进行超时判断 + * + * @param time 时间数值 + * @param unit 时间单位 + */ + default WorkerWrapperBuilder timeout(long time, TimeUnit unit) { + return timeout(true, time, unit); + } + + default WorkerWrapperBuilder timeout(boolean enableTimeOut, long time, TimeUnit unit) { + return setTimeOut().enableTimeOut(enableTimeOut).setTime(time, unit).end(); + } + + // 略 +} +``` + + + +#### 测试示例 + +示例: + +```java +class Case8 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })); + } + + /** + * A ==> B(10ms) ==> C(20ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A") + .nextOf(builder("B", 10) + .nextOf(builder("C", 20).build()) + .build()) .build(); + Async.work(20, a).awaitFinish(); + /* 输出: + wrapper(id=A) has begin . + wrapper(id=A) is working + wrapper(id=A) callback success , workResult is WorkResult{result=null, resultState=SUCCESS, ex=null} + wrapper(id=B) has begin . + wrapper(id=B) is working + wrapper(id=B) callback success , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} + wrapper(id=C) has begin . + wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} + java.lang.InterruptedException: sleep interrupted + at java.lang.Thread.sleep(Native Method) + ... + 以下异常信息省略 + */ + } +} ``` +### 设置是否允许被打断线程 +可通过该选项去设置允许线程被打断: -3. 复杂点的 +```java +public interface WorkerWrapperBuilder { + // 略 + + /** + * 是否允许被试图中断线程 + * + * @param allow 是则true + */ + WorkerWrapperBuilder allowInterrupt(boolean allow); + + // 略 +} +``` -![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140445_8d52e4d6_303698.png "屏幕截图.png") +#### 线程会被打断的具体情况 -在测试类里能找到,下图是执行结果。看时间戳,就知道执行的顺序。每个执行单元都是睡1秒。 +开启之后,在以下情况,会试图打断正处于WORKING状态的工作线程。 -![输入图片说明](https://images.gitee.com/uploads/images/2019/1225/133828_0c76624c_303698.png "屏幕截图.png") +* 总任务超时,但本wrapper在WORKING。 +* 单wrapper超时,但本wrapper在WORKING。 +* wrapper应当被跳过,但本wrapper在WORKING。 +* 调用`WorkerWrapper#failNow()`方法,且wrapper在WORKING状态。 -4. 依赖别的worker执行结果作为入参 +# 开放工具类 -可以从action的参数中根据wrapper的id获取任意一个执行单元的执行结果,但请注意执行顺序,如果尚未执行,则在调用WorkerResult.getResult()会得到null! -![输入图片说明](https://images.gitee.com/uploads/images/2020/0511/215924_28af8655_303698.png "屏幕截图.png")![输入图片说明](https://images.gitee.com/uploads/images/2020/0511/215933_12e13dba_303698.png "屏幕截图.png") +> `asyncTool-openutil`工具模块提供了一些便于开发的工具类。 +> +> 可单独引入依赖: +> +> ```xml +> +> +> com.jd.platform +> asyncTool-openutil +> 1.5.1-SNAPSHOT +> +> +> ``` -5. 其他的详见test包下的测试类,支持各种形式的组合、编排。 +### 集合类 + +> 普通集合包`com.jd.platform.async.openutil.collection.*` + +这里不详述,要用的话源码里有注释。 + +* `SparseArray2D` 稀疏矩阵。 +* `CommonDirectedGraph` 有向图。 +* `CommonStoreArk` id储物柜。 + +### 定时器 + +> `com.jd.platform.async.openutil.timer.*` + +* `HashedWheelTimer` 从netty里抄来的时间轮工具类。 +### 其他 +> `com.jd.platform.async.openutil.*` +* `BiInt` 一个表示两个int值的实体类,内含缓冲区间、默认比较器,适合拿来当2维索引。 diff --git a/README.md b/README.md index 2a7233d..24f2ab0 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,16 @@ 在V1.3后,框架支持在worker的action的入参Map中获取任意一个执行单元的执行结果,当然,可以取其中的1个、多个执行结果作为自己的入参。Key就是在定义wrapper时通过id传进来的唯一id标识。详情demo可以查看test包下dependnew包案例。 -## 并发场景可能存在的需求之——全组任务的超时 -一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。 +## 并发场景可能存在的需求之——任务的超时 +> 在v1.4中: +> +> 一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。 + +在v1.5中: + +每个wrapper可以设置自己的超时时间,同时也可以设置整组任务的超时时间。 + +且可以设置一旦超时则打断线程(默认不启用打断线程) ## 并发场景可能存在的需求之——高性能、低线程数 该框架全程无锁,不依靠线程锁来保证顺序。 @@ -114,4 +122,3 @@ ## 快速开始 [点此开启实战](https://gitee.com/jd-platform-opensource/asyncTool/blob/master/QuickStart.md) - diff --git a/asyncTool-core/pom.xml b/asyncTool-core/pom.xml new file mode 100644 index 0000000..0838a91 --- /dev/null +++ b/asyncTool-core/pom.xml @@ -0,0 +1,27 @@ + + + + asyncTool + com.jd.platform + 1.5.1-SNAPSHOT + + 4.0.0 + + asyncTool-core + + + 8 + 8 + + + + + com.jd.platform + asyncTool-openutil + 1.5.1-SNAPSHOT + + + + \ No newline at end of file diff --git a/src/main/java/com/jd/platform/async/callback/DefaultCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java similarity index 66% rename from src/main/java/com/jd/platform/async/callback/DefaultCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java index 8b1d6c5..e187359 100755 --- a/src/main/java/com/jd/platform/async/callback/DefaultCallback.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultCallback.java @@ -15,14 +15,10 @@ public class DefaultCallback implements ICallback { } /** - * 默认将打印存在的非{@link com.jd.platform.async.exception.SkippedException}的异常。 + * 默认情况啥回调都没有,而且将吞掉所有异常显示(只保存在{@link WorkResult}中) */ @Override public void result(boolean success, T param, WorkResult workResult) { - Exception ex = workResult.getEx(); - if (ex != null && !(ex instanceof SkippedException)) { - ex.printStackTrace(); - } + // do nothing } - } diff --git a/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/DefaultGroupCallback.java diff --git a/src/main/java/com/jd/platform/async/callback/ICallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java similarity index 49% rename from src/main/java/com/jd/platform/async/callback/ICallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java index ee71448..fe0b505 100755 --- a/src/main/java/com/jd/platform/async/callback/ICallback.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java @@ -1,6 +1,7 @@ package com.jd.platform.async.callback; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.worker.WorkResult; /** @@ -11,7 +12,6 @@ import com.jd.platform.async.worker.WorkResult; */ @FunctionalInterface public interface ICallback { - /** * 任务开始的监听 */ @@ -25,4 +25,22 @@ public interface ICallback { * 只要Wrapper被调用后成功或失败/超时,该方法都会被执行。 */ void result(boolean success, T param, WorkResult workResult); + + /** + * 提供常量选项:打印异常信息,跳过时的异常{@link SkippedException}不会打印。 + */ + ICallback PRINT_EXCEPTION_STACK_TRACE = new ICallback() { + @Override + public void result(boolean success, Object param, WorkResult workResult) { + Exception ex = workResult.getEx(); + if (ex != null && !(ex instanceof SkippedException)) { + ex.printStackTrace(); + } + } + + @Override + public String toString() { + return "PRINT_EXCEPTION_STACK_TRACE"; + } + }; } diff --git a/src/main/java/com/jd/platform/async/callback/IGroupCallback.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IGroupCallback.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/IGroupCallback.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/IGroupCallback.java diff --git a/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java similarity index 100% rename from src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/ITimeoutWorker.java diff --git a/src/main/java/com/jd/platform/async/callback/IWorker.java b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java similarity index 80% rename from src/main/java/com/jd/platform/async/callback/IWorker.java rename to asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java index ffe000a..5f74c45 100755 --- a/src/main/java/com/jd/platform/async/callback/IWorker.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/callback/IWorker.java @@ -1,9 +1,9 @@ package com.jd.platform.async.callback; -import java.util.Map; - import com.jd.platform.async.wrapper.WorkerWrapper; +import java.util.Map; + /** * 每个最小执行单元需要实现该接口 * @@ -17,10 +17,10 @@ public interface IWorker { * @param object object * @param allWrappers 任务包装 */ - V action(T object, Map allWrappers); + V action(T object, Map> allWrappers); /** - * 超时、异常时,返回的默认值 + * 超时、异常、跳过时,返回的默认值 * * @return 默认值 */ diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java b/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java new file mode 100644 index 0000000..5532b74 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/exception/CancelSkippedException.java @@ -0,0 +1,19 @@ +package com.jd.platform.async.exception; + +/** + * 整组取消,设置该异常。 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午6:12 + */ +public class CancelSkippedException extends SkippedException { + public CancelSkippedException() { + } + + public CancelSkippedException(String message) { + super(message); + } + + public CancelSkippedException(String message, long skipAt) { + super(message, skipAt); + } +} diff --git a/src/main/java/com/jd/platform/async/exception/SkippedException.java b/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java similarity index 55% rename from src/main/java/com/jd/platform/async/exception/SkippedException.java rename to asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java index 807e8ec..1c38e7a 100644 --- a/src/main/java/com/jd/platform/async/exception/SkippedException.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/exception/SkippedException.java @@ -1,16 +1,30 @@ package com.jd.platform.async.exception; +import com.jd.platform.async.executor.timer.SystemClock; + /** * 如果任务在执行之前,自己后面的任务已经执行完或正在被执行,则抛该exception + * * @author wuweifeng wrote on 2020-02-18 * @version 1.0 */ public class SkippedException extends RuntimeException { + private final long skipAt; + public SkippedException() { - super(); + this(null); } public SkippedException(String message) { + this(message, SystemClock.now()); + } + + public SkippedException(String message, long skipAt) { super(message); + this.skipAt = skipAt; + } + + public long getSkipAt() { + return skipAt; } } diff --git a/src/main/java/com/jd/platform/async/executor/Async.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java similarity index 59% rename from src/main/java/com/jd/platform/async/executor/Async.java rename to asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java index 7dcf855..c0c3d88 100644 --- a/src/main/java/com/jd/platform/async/executor/Async.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/Async.java @@ -4,12 +4,15 @@ package com.jd.platform.async.executor; import com.jd.platform.async.callback.DefaultGroupCallback; import com.jd.platform.async.callback.IGroupCallback; import com.jd.platform.async.executor.timer.SystemClock; +import com.jd.platform.async.worker.OnceWork; import com.jd.platform.async.wrapper.WorkerWrapper; -import com.jd.platform.async.wrapper.WrapperEndingInspector; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; +import com.sun.istack.internal.Nullable; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** @@ -23,61 +26,90 @@ public class Async { // ========================= 任务执行核心代码 ========================= /** - * 出发点 + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。 + */ + public static OnceWork work(long timeout, + Collection> workerWrappers) { + return work(timeout, getCommonPool(), workerWrappers); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 可变参式传入。使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。 + */ + public static OnceWork work(long timeout, + WorkerWrapper... workerWrappers) { + return work(timeout, getCommonPool(), workerWrappers); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 可变参式传入。使用uuid作为工作id。 + */ + public static OnceWork work(long timeout, + ExecutorService executorService, + WorkerWrapper... workerWrappers) { + return work(timeout, executorService, Arrays.asList( + Objects.requireNonNull(workerWrappers, "workerWrappers array is null"))); + } + + /** + * {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。 + * 省略工作id,使用uuid。 + */ + public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers) { + return work(timeout, executorService, workerWrappers, UUID.randomUUID().toString()); + } + + /** + * 核心方法。 + * 该方法不是同步阻塞执行的。如果想要同步阻塞执行,则调用返回值的{@link OnceWork#awaitFinish()}即可。 * - * @return 只要执行未超时,就返回true。 + * @param timeout 全组超时时间 + * @param executorService 执行线程池 + * @param workerWrappers 任务容器集合 + * @param workId 本次工作id + * @return 返回 {@link OnceWork}封装对象。 */ - public static boolean beginWork(long timeout, - ExecutorService executorService, - Collection> workerWrappers) - throws InterruptedException { - if (workerWrappers == null || workerWrappers.size() == 0) { - return false; + public static OnceWork work(long timeout, + ExecutorService executorService, + Collection> workerWrappers, + String workId) { + if (workerWrappers == null || workerWrappers.isEmpty()) { + return OnceWork.emptyWork(workId); } //保存上次执行的线程池变量(为了兼容以前的旧功能) - Async.lastExecutorService = Objects.requireNonNull(executorService, "ExecutorService is null ! "); - //定义一个map,存放所有的wrapper,key为wrapper的唯一id,value是该wrapper,可以从value中获取wrapper的result - final ConcurrentMap> forParamUseWrappers = - new ConcurrentHashMap<>(Math.max(workerWrappers.size() * 3, 8)); - final WrapperEndingInspector inspector = new WrapperEndingInspector(SystemClock.now() + timeout); - inspector.addWrapper(workerWrappers); + Async.lastExecutorService.set(Objects.requireNonNull(executorService, "ExecutorService is null ! ")); + final WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout); + final OnceWork.Impl onceWork = new OnceWork.Impl(group, workId); + group.addWrapper(workerWrappers); workerWrappers.forEach(wrapper -> { if (wrapper == null) { return; } - executorService.submit(() -> wrapper.work(executorService, timeout, forParamUseWrappers, inspector)); + executorService.submit(() -> wrapper.work(executorService, timeout, group)); }); - inspector.registerToPollingCenter(); - return inspector.await(); - //处理超时的逻辑被移动到了WrapperEndingInspector中。 + return onceWork; } /** - * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL - */ - public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) - throws ExecutionException, InterruptedException { - if (workerWrapper == null || workerWrapper.length == 0) { - return false; - } - Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet()); - return beginWork(timeout, executorService, workerWrappers); - } - - /** - * 同步阻塞,直到所有都完成,或失败 + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 */ - public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { - return beginWork(timeout, getCommonPool(), workerWrapper); - } - + @SuppressWarnings("unused") + @Deprecated public static void beginWorkAsync(long timeout, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) { beginWorkAsync(timeout, getCommonPool(), groupCallback, workerWrapper); } /** * 异步执行,直到所有都完成,或失败后,发起回调 + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 */ + @Deprecated public static void beginWorkAsync(long timeout, ExecutorService executorService, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) { if (groupCallback == null) { groupCallback = new DefaultGroupCallback(); @@ -128,7 +160,7 @@ public class Async { * 该线程池将会给线程取名为asyncTool-commonPool-thread-0(数字不重复)。 *

*/ - private static ThreadPoolExecutor COMMON_POOL; + private static volatile ThreadPoolExecutor COMMON_POOL; /** * 在以前(及现在)的版本中: @@ -136,8 +168,11 @@ public class Async { *

* 注意,这里是个static,也就是只能有一个线程池。用户自定义线程池时,也只能定义一个 */ - private static volatile ExecutorService lastExecutorService; + private static final AtomicReference lastExecutorService = new AtomicReference<>(null); + /** + * 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。 + */ public static ThreadPoolExecutor getCommonPool() { if (COMMON_POOL == null) { synchronized (Async.class) { @@ -151,9 +186,11 @@ public class Async { new ThreadFactory() { private final AtomicLong threadCount = new AtomicLong(0); + @SuppressWarnings("NullableProblems") @Override public Thread newThread(Runnable r) { - Thread t = new Thread(r, "asyncTool-commonPool-thread-" + threadCount.getAndIncrement()); + Thread t = new Thread(r, + "asyncTool-commonPool-thread-" + threadCount.getAndIncrement()); t.setDaemon(true); return t; } @@ -181,7 +218,15 @@ public class Async { ",largestCount=" + COMMON_POOL.getLargestPoolSize(); } - public static synchronized void shutDownCommonPool(boolean now) { + /** + * @param now 是否立即关闭 + * @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。 + */ + @SuppressWarnings("unused") + public static synchronized boolean shutDownCommonPool(boolean now) { + if (COMMON_POOL == null) { + return false; + } if (!COMMON_POOL.isShutdown()) { if (now) { COMMON_POOL.shutdownNow(); @@ -189,6 +234,52 @@ public class Async { COMMON_POOL.shutdown(); } } + return true; + } + + // ========================= deprecated ========================= + + /** + * 同步执行一次任务。 + * + * @return 只要执行未超时,就返回true。 + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, + ExecutorService executorService, + Collection> workerWrappers) + throws InterruptedException { + final OnceWork work = work(timeout, executorService, workerWrappers); + work.awaitFinish(); + return work.hasTimeout(); + } + + /** + * 同步执行一次任务。 + * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) + throws ExecutionException, InterruptedException { + if (workerWrapper == null || workerWrapper.length == 0) { + return false; + } + Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet()); + //noinspection unchecked + return beginWork(timeout, executorService, workerWrappers); + } + + /** + * 同步阻塞,直到所有都完成,或失败 + * + * @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。 + */ + @Deprecated + public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { + return beginWork(timeout, getCommonPool(), workerWrapper); } /** @@ -202,8 +293,9 @@ public class Async { */ @Deprecated public static void shutDown() { - if (lastExecutorService != COMMON_POOL) { - shutDown(lastExecutorService); + final ExecutorService last = lastExecutorService.get(); + if (last != COMMON_POOL) { + shutDown(last); } } @@ -214,7 +306,7 @@ public class Async { * @deprecated 没啥用的方法,要关闭线程池还不如直接调用线程池的关闭方法,避免歧义。 */ @Deprecated - public static void shutDown(ExecutorService executorService) { + public static void shutDown(@Nullable ExecutorService executorService) { if (executorService != null) { executorService.shutdown(); } diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java new file mode 100644 index 0000000..2dc1829 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/PollingCenter.java @@ -0,0 +1,87 @@ +package com.jd.platform.async.executor; + +import com.jd.platform.async.openutil.timer.HashedWheelTimer; +import com.jd.platform.async.openutil.timer.Timeout; +import com.jd.platform.async.openutil.timer.Timer; +import com.jd.platform.async.openutil.timer.TimerTask; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * 检查{@link WorkerWrapperGroup}是否调用完成的轮询中心。 + * 内部使用时间轮进行轮询。 + *

+ * =========================================================================================== + *

+ * 在v1.4及以前的版本,存在如下问题: + * > + * 在使用线程数量较少的线程池进行beginWork时,调用WorkerWrapper#beginNext方法时, + * 会因为本线程等待下游Wrapper执行完成而存在线程耗尽bug。线程池会死翘翘的僵住、动弹不得。 + * > + * 例如仅有2个线程的线程池,执行以下任务: + * {@code + *

+ * 这是旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况,在test/v15.wrappertest中示例testThreadPolling_V14Bug说明了这个bug + * 线程数:2 + * A(5ms)--B1(10ms) ---|--> C1(5ms) + * . \ | (B1、B2任一完成可执行C1、C2) + * . ---> B2(20ms) --|--> C2(5ms) + *

+ * } + * 线程1执行了A,然后在{@link CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。 + * 线程2执行了B1或B2中的一个,也在allOf方法等待C1、C2完成。 + * 结果没有线程执行C和B2了,导致超时而死,并且这个线程池线程有可能被耗尽。 + * > + * + * @author create by TcSnZh on 2021/5/9-下午9:22 + */ +public class PollingCenter { + + // ========== singleton instance ========== + + private static final PollingCenter instance = new PollingCenter(); + + public static PollingCenter getInstance() { + return instance; + } + + // ========== fields and methods ========== + + public void checkGroup(WorkerWrapperGroup.CheckFinishTask task) { + checkGroup(task, 0); + } + + public void checkGroup(WorkerWrapperGroup.CheckFinishTask task, long daley) { + timer.newTimeout(task, daley, TimeUnit.MILLISECONDS); + } + + private final Timer timer = new Timer() { + private final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer( + r -> { + Thread thread = new Thread(r, "asyncTool-pollingThread"); + thread.setDaemon(true); + return thread; + }, + 1, + TimeUnit.MILLISECONDS, + 1024); + + @Override + public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) { + return hashedWheelTimer.newTimeout(task, delay, unit); + } + + @Override + public Set stop() { + return hashedWheelTimer.stop(); + } + + @Override + public String toString() { + return "PollingCenter.timer"; + } + }; +} diff --git a/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java b/asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java similarity index 97% rename from src/main/java/com/jd/platform/async/executor/timer/SystemClock.java rename to asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java index e65dd85..6cba50a 100644 --- a/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/executor/timer/SystemClock.java @@ -2,7 +2,6 @@ package com.jd.platform.async.executor.timer; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java new file mode 100644 index 0000000..611638b --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/worker/OnceWork.java @@ -0,0 +1,375 @@ +package com.jd.platform.async.worker; + +import com.jd.platform.async.executor.timer.SystemClock; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; + +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 一次工作结果的总接口。 + * + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午3:22 + */ +public interface OnceWork { + /** + * 返回唯一的workId + */ + String workId(); + + /** + * 判断是否结束。因超时而结束也算结束。 + */ + boolean isFinish(); + + /** + * 同步等待到结束。 + */ + void awaitFinish() throws InterruptedException; + + /** + * 判断是否超时 + * + * @return 如果尚未结束或已结束但未超时,返回false。已结束且已经超时返回true。 + */ + boolean hasTimeout(); + + /** + * 判断是否全部wrapper都处于 执行成功 或 跳过。 + * + * @return 如果已经结束,所有wrapper都成功或跳过返回true,否则返回false。如果尚未结束,返回false。 + */ + default boolean allSuccess() { + if (!isFinish()) { + return false; + } + return getWrappers().values().stream().allMatch(wrapper -> { + final ResultState state = wrapper.getWorkResult().getResultState(); + return state == ResultState.SUCCESS || state == ResultState.DEFAULT; + }); + } + + /** + * 获取全部参与到工作中的wrapper。 + */ + Map> getWrappers(); + + /** + * 获取{@link WorkResult#getResultState()}为{@link ResultState#SUCCESS}的wrapper。 + */ + default Map> getSuccessWrappers() { + return getWrappersOfState(ResultState.SUCCESS); + } + + /** + * 获取状态于这些state中的wrapper。 + * + * @param ofState 状态列表 + * @return 返回Map + */ + default Map> getWrappersOfState(ResultState... ofState) { + final HashSet states = new HashSet<>(Arrays.asList(ofState)); + return getWrappers().entrySet().stream() + .filter(entry -> states.contains(entry.getValue().getWorkResult().getResultState())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + /** + * 获取启动时间 + */ + long getStartTime(); + + /** + * 获取结束时间 + * + * @return 如果超时,返回超时的时刻。如果尚未结束,则抛出异常。 + * @throws IllegalStateException 尚未结束,抛出异常。 + */ + long getFinishTime(); + + /** + * @return 已经取消完成 + */ + boolean isCancelled(); + + /** + * @return 是否正在取消中 + */ + boolean isWaitingCancel(); + + /** + * 请求异步取消。 + */ + void pleaseCancel(); + + /** + * 同步等待取消完成。 + */ + default void pleaseCancelAndAwaitFinish() throws InterruptedException { + if (!isCancelled() && !isWaitingCancel()) { + pleaseCancel(); + } + awaitFinish(); + } + + /** + * @return 返回 {@link AsFuture}封装对象。 + */ + default AsFuture asFuture() { + return new AsFuture(this, limitTime -> limitTime / 16); + } + + /** + * @param sleepCheckInterval 为防止线程爆炸,在{@link Future#get(long, TimeUnit)}方法时使用隔一段时间检查一次。 + * 该Function的参数为总超时毫秒值,返回值为检查时间间隔。 + * @return 返回 {@link AsFuture}封装对象。 + */ + default AsFuture asFuture(Function sleepCheckInterval) { + return new AsFuture(this, sleepCheckInterval); + } + + // static + + /** + * 空任务 + */ + static OnceWork emptyWork(String workId) { + return new EmptyWork(workId); + } + + // class + + class AsFuture implements Future>> { + private final OnceWork onceWork; + private final Function sleepCheckInterval; + + private AsFuture(OnceWork onceWork, Function sleepCheckInterval) { + this.onceWork = onceWork; + this.sleepCheckInterval = sleepCheckInterval; + } + + /** + * 同步等待取消 + * + * @param ignore__mayInterruptIfRunning 该参数将被无视。因为暂未实现“修改允许打断属性”功能。 // todo 等待实现 + */ + @Override + public boolean cancel(boolean ignore__mayInterruptIfRunning) { + try { + onceWork.pleaseCancelAndAwaitFinish(); + } catch (InterruptedException e) { + throw new RuntimeException("", e); + } + return true; + } + + @Override + public boolean isCancelled() { + return onceWork.isCancelled(); + } + + @Override + public boolean isDone() { + return onceWork.isFinish(); + } + + @Override + public Map> get() throws InterruptedException, ExecutionException { + if (!onceWork.isFinish()) { + onceWork.awaitFinish(); + } + return onceWork.getWrappers(); + } + + /** + * 避免线程爆炸,该方法不予单独开线程,而是单线程{@link Thread#sleep(long)}每睡一段时间检查一次。 + */ + @Override + public Map> get(long timeout, + @SuppressWarnings("NullableProblems") TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + final long millis = Objects.requireNonNull(unit).toMillis(timeout); + final long interval = Math.max(1, Math.min(millis, sleepCheckInterval.apply(millis))); + for (int i = 0; interval * i < millis; i++) { + if (onceWork.isFinish()) { + return onceWork.getWrappers(); + } + Thread.sleep(interval); + } + throw new TimeoutException( + "onceWork.asFuture.get(long,TimeUnit) out of time limit(" + + timeout + "," + unit + ") , this is " + this); + } + + @Override + public String toString() { + return "(asFuture from " + this + ")"; + } + } + + abstract class AbstractOnceWork implements OnceWork { + protected final String workId; + + public AbstractOnceWork(String workId) { + this.workId = workId; + } + + @Override + public String workId() { + return workId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OnceWork)) { + return false; + } + OnceWork _o = (OnceWork) o; + return Objects.equals(_o.workId(), this.workId()); + } + + @Override + public int hashCode() { + return workId().hashCode(); + } + + @Override + public String toString() { + final boolean finish; + final StringBuilder sb = new StringBuilder(48) + .append(this.getClass().getSimpleName()) + .append("{isFinish=").append(finish = isFinish()) + .append(", hasTimeout=").append(hasTimeout()) + .append(", allSuccess=").append(allSuccess()) + .append(", getStartTime=").append(getStartTime()) + .append(", isCancelled=").append(isCancelled()) + .append(", isWaitingCancel=").append(isWaitingCancel()); + if (finish) { + sb.append(", getFinishTime=").append(getFinishTime()); + } + return sb + .append(", wrappers::getId=").append(getWrappers().keySet()) + .append('}').toString(); + } + } + + class Impl extends AbstractOnceWork { + protected final WorkerWrapperGroup group; + + public Impl(WorkerWrapperGroup group, String workId) { + super(workId); + this.group = group; + } + + @Override + public boolean isFinish() { + return group.getEndCDL().getCount() > 0; + } + + @Override + public void awaitFinish() throws InterruptedException { + group.getEndCDL().await(); + } + + @Override + public boolean hasTimeout() { + return group.getAnyTimeout().get(); + } + + @Override + public Map> getWrappers() { + return group.getForParamUseWrappers(); + } + + @Override + public long getStartTime() { + return group.getGroupStartTime(); + } + + @Override + public long getFinishTime() { + if (isFinish()) { + throw new IllegalStateException("work not finish."); + } + return group.getFinishTime(); + } + + @Override + public boolean isCancelled() { + return group.isCancelled(); + } + + @Override + public boolean isWaitingCancel() { + return group.isWaitingCancel(); + } + + @Override + public void pleaseCancel() { + group.pleaseCancel(); + } + } + + class EmptyWork extends AbstractOnceWork { + private final long initTime = SystemClock.now(); + + public EmptyWork(String workId) { + super(workId); + } + + @Override + public boolean isFinish() { + return true; + } + + @Override + public void awaitFinish() { + // do nothing + } + + @Override + public boolean hasTimeout() { + return false; + } + + @Override + public Map> getWrappers() { + return Collections.emptyMap(); + } + + @Override + public long getStartTime() { + return initTime; + } + + @Override + public long getFinishTime() { + return initTime; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isWaitingCancel() { + return false; + } + + @Override + public void pleaseCancel() { + } + + @Override + public String toString() { + return "(it's empty work)"; + } + } +} diff --git a/src/main/java/com/jd/platform/async/worker/ResultState.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/ResultState.java similarity index 100% rename from src/main/java/com/jd/platform/async/worker/ResultState.java rename to asyncTool-core/src/main/java/com/jd/platform/async/worker/ResultState.java diff --git a/src/main/java/com/jd/platform/async/worker/WorkResult.java b/asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java similarity index 70% rename from src/main/java/com/jd/platform/async/worker/WorkResult.java rename to asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java index 95e5868..1cf0f04 100755 --- a/src/main/java/com/jd/platform/async/worker/WorkResult.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/worker/WorkResult.java @@ -7,12 +7,12 @@ public class WorkResult { /** * 执行的结果 */ - private V result; + private final V result; /** * 结果状态 */ - private ResultState resultState; - private Exception ex; + private final ResultState resultState; + private final Exception ex; public WorkResult(V result, ResultState resultState) { this(result, resultState, null); @@ -24,10 +24,15 @@ public class WorkResult { this.ex = ex; } + /** + * 返回不可修改的DEFAULT单例。 + */ public static WorkResult defaultResult() { - return new WorkResult<>(null, ResultState.DEFAULT); + return (WorkResult) DEFAULT; } + private static final WorkResult DEFAULT = new WorkResult<>(null, ResultState.DEFAULT); + @Override public String toString() { return "WorkResult{" + @@ -41,23 +46,11 @@ public class WorkResult { return ex; } - public void setEx(Exception ex) { - this.ex = ex; - } - public V getResult() { return result; } - public void setResult(V result) { - this.result = result; - } - public ResultState getResultState() { return resultState; } - - public void setResultState(ResultState resultState) { - this.resultState = resultState; - } } diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java new file mode 100644 index 0000000..fd5aa17 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/QuickBuildWorkerWrapper.java @@ -0,0 +1,100 @@ +package com.jd.platform.async.wrapper; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.openutil.collection.DirectedGraph; +import com.jd.platform.async.openutil.collection.Graph; +import com.jd.platform.async.wrapper.strategy.WrapperStrategy; + +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 快速构造{@link WorkerWrapper},少废话! + *

+ * 直接设置属性,不用麻烦Builder设置来设置去, + * 请 注 意:构造方法不会检查参数合法性,请程序员自己保证参数合法。 + *

+ * 将关系存储于有向图中{@link DirectedGraph}以节省每个wrapper都要保存节点数据的开销。 + *

+ * + * @author create by TcSnZh on 2021/5/13-上午11:54 + */ +public class QuickBuildWorkerWrapper extends WorkerWrapper { + private final DirectedGraph, Object> graph; + + private volatile Set> nextWrappersCache; + private volatile Set> dependWrappersCache; + + /** + * 构造函数,传入所有属性 + * + * @param id {@link WorkerWrapper#id} + * @param param {@link WorkerWrapper#param} + * @param worker {@link WorkerWrapper#worker} + * @param callback {@link WorkerWrapper#callback} + * @param allowInterrupt {@link WorkerWrapper#allowInterrupt} + * @param enableTimeout {@link WorkerWrapper#enableTimeout} + * @param timeoutLength {@link WorkerWrapper#timeoutLength} + * @param timeoutUnit {@link WorkerWrapper#timeoutLength} + * @param wrapperStrategy {@link WorkerWrapper#timeoutUnit} + * @param wrapperGraph 将节点信息保存在图中,而不是如{@link StableWorkerWrapper}在每个wrapper中都保存节点信息。 + *

+ * {@link WorkerWrapper#getDependWrappers()}与{@link WorkerWrapper#getNextWrappers()}方法 + * 将从本图中读取依赖顺序。除此之外,本类不会对本图进行任何修改操作。 + * 因此,传入的此图应当保证读取时的线程安全。 + *

+ */ + public QuickBuildWorkerWrapper(String id, + T param, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit, + WrapperStrategy wrapperStrategy, + DirectedGraph, Object> wrapperGraph) { + super(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, wrapperStrategy); + graph = wrapperGraph; + super.param = param; + State.setState(state, State.BUILDING, State.INIT); + } + + @Override + public Set> getNextWrappers() { + if (nextWrappersCache == null) { + synchronized (this) { + if (nextWrappersCache == null) { + nextWrappersCache = graph.getRelationFrom(this).stream() + .map(Graph.Entry::getTo).collect(Collectors.toSet()); + } + } + } + return nextWrappersCache; + } + + @Override + public Set> getDependWrappers() { + if (dependWrappersCache == null) { + synchronized (this) { + if (dependWrappersCache == null) { + dependWrappersCache = graph.getRelationTo(this).stream() + .map(Graph.Entry::getFrom).collect(Collectors.toSet()); + } + } + } + return dependWrappersCache; + } + + @Override + void setNextWrappers(Set> nextWrappers) { + throw new UnsupportedOperationException(); + } + + @Override + void setDependWrappers(Set> dependWrappers) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java similarity index 73% rename from src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java index bda426a..8dc949d 100644 --- a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java @@ -4,15 +4,22 @@ import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; import java.util.Set; +import java.util.concurrent.TimeUnit; /** * {@link WorkerWrapper}默认实现类,将上下游Wrapper保存在自己的Set中。 * * @author create by TcSnZh on 2021/5/6-下午2:41 */ -class StableWorkerWrapper extends WorkerWrapper { - StableWorkerWrapper(String id, IWorker worker, T param, ICallback callback) { - super(id, worker, param, callback); +public class StableWorkerWrapper extends WorkerWrapper { + public StableWorkerWrapper(String id, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit) { + super(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit); } /** @@ -45,7 +52,6 @@ class StableWorkerWrapper extends WorkerWrapper { return nextWrappers; } - // ========== package impl ========== @Override @@ -54,7 +60,7 @@ class StableWorkerWrapper extends WorkerWrapper { } @Override - Set> getDependWrappers() { + public Set> getDependWrappers() { return dependWrappers; } diff --git a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java similarity index 84% rename from src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java index 5f98330..5c0da4a 100644 --- a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java @@ -1,10 +1,14 @@ package com.jd.platform.async.wrapper; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.worker.WorkResult; -import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy; -import com.jd.platform.async.wrapper.actionstrategy.*; +import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; import java.util.*; import java.util.concurrent.TimeUnit; @@ -59,15 +63,15 @@ class StableWorkerWrapperBuilder, DependWrapperActionStrategy> dependWrapperActionStrategyMap; + private Map, DependOnUpWrapperStrategy> dependWrapperActionStrategyMap; /** * 存储需要特殊对待自己的nextWrapper集合。 */ - private Map, DependWrapperActionStrategy> selfIsSpecialMap; + private Map, DependOnUpWrapperStrategy> selfIsSpecialMap; /** * 一个保存以must=true方式传入的WorkerWrapper的集合。 *

- * 该Set将会加入到{@link WorkerWrapper.WrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中 + * 该Set将会加入到{@link WorkerWrapper.StableWrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中 */ private Set> mustDependSet; /** @@ -93,6 +97,9 @@ class StableWorkerWrapperBuilder wrapper) { + public SetDependImpl specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper) { if (strategy == null || wrapper == null) { return this; } @@ -226,7 +233,7 @@ class StableWorkerWrapperBuilder wrapper) { + public SetNextImpl specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper) { if (strategy == null || wrapper == null) { return this; } @@ -259,33 +266,48 @@ class StableWorkerWrapperBuilder 0, unit=" + unit + " must not null"); + throw new IllegalStateException("Illegal argument : time=" + time + " must > 0, unit=" + unit + " require not null"); } StableWorkerWrapperBuilder.this.time = time; StableWorkerWrapperBuilder.this.unit = unit; return this; } - @Override - public SetTimeOutImpl allowInterrupt(boolean allow) { - StableWorkerWrapperBuilder.this.allowInterrupt = allow; - return this; - } - @Override public BUILDER_SUB_CLASS end() { return returnThisBuilder(); } } + @Override + public WorkerWrapperBuilder allowInterrupt(boolean allow) { + allowInterrupt = allow; + return returnThisBuilder(); + } + @Override public WorkerWrapper build() { isBuilding = true; + // ========== 设置单wrapper超时检查 ========== + { + if (enableTimeOut) { + if (time <= 0) { + throw new IllegalArgumentException("timeout time " + time + " must > 0"); + } + if (unit == null) { + throw new IllegalArgumentException(new NullPointerException("timeout unit require not null")); + } + } + } + // ========== 构造wrapper ========== WorkerWrapper wrapper = new StableWorkerWrapper<>( id == null ? UUID.randomUUID().toString() : id, worker, - param, - callback + callback, + allowInterrupt, + enableTimeOut, + time, + unit ); wrapper.setDependWrappers(new LinkedHashSet<>()); wrapper.setNextWrappers(new LinkedHashSet<>()); @@ -309,6 +331,7 @@ class StableWorkerWrapperBuilder 0) { @@ -320,7 +343,7 @@ class StableWorkerWrapperBuilder 0) { - DependWrapperStrategyMapper mapper = new DependWrapperStrategyMapper(); + DependOnUpWrapperStrategyMapper mapper = new DependOnUpWrapperStrategyMapper(); dependWrapperActionStrategyMap.forEach(mapper::putMapping); wrapper.getWrapperStrategy().setDependWrapperStrategyMapper(mapper); } @@ -329,8 +352,14 @@ class StableWorkerWrapperBuilder mustMapper.addDependMust(wrapper))); } if (selfIsSpecialMap != null && selfIsSpecialMap.size() > 0) { - selfIsSpecialMap.forEach((next, strategy) -> Optional.ofNullable(next.getWrapperStrategy().getDependWrapperStrategyMapper()) - .ifPresent(wrapperMapper -> wrapperMapper.putMapping(wrapper, strategy))); + selfIsSpecialMap.forEach((next, strategy) -> { + DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper(); + if (dependOnUpWrapperStrategyMapper == null) { + next.getWrapperStrategy().setDependWrapperStrategyMapper( + dependOnUpWrapperStrategyMapper = new DependOnUpWrapperStrategyMapper()); + } + dependOnUpWrapperStrategyMapper.putMapping(wrapper, strategy); + }); } } // ========== 设置检查是否跳过策略 ========== @@ -344,18 +373,9 @@ class StableWorkerWrapperBuilder " + 0); - } - if (unit == null) { - throw new IllegalStateException("timeout unit must not null"); - } - wrapper.setTimeOut(new WorkerWrapper.TimeOutProperties(true, time, unit, allowInterrupt, wrapper)); - } - } + // ========== end ========== + wrapper.state.set(WorkerWrapper.State.INIT.id); + wrapper.setParam(param); return wrapper; } @@ -363,8 +383,8 @@ class StableWorkerWrapperBuilder... wrappers) { @@ -379,8 +399,8 @@ class StableWorkerWrapperBuilder wrapper) { @@ -388,7 +408,7 @@ class StableWorkerWrapperBuilder wrapper, boolean isMust) { @@ -459,7 +479,7 @@ class StableWorkerWrapperBuilder + * v1.5时将其抽取为抽象类,以解耦并提高扩展性。 + * + * @author wuweifeng wrote on 2019-11-19. + */ +@SuppressWarnings("AlibabaAbstractClassShouldStartWithAbstractNaming") +public abstract class WorkerWrapper { + // ========== 固定属性 ========== + + /** + * 该wrapper的唯一标识 + */ + protected final String id; + protected final IWorker worker; + protected final ICallback callback; + /** + * 各种策略的封装类。 + */ + private final WrapperStrategy wrapperStrategy; + /** + * 是否允许被打断 + */ + protected final boolean allowInterrupt; + /** + * 是否启动超时检查 + */ + final boolean enableTimeout; + /** + * 超时时间长度 + */ + final long timeoutLength; + /** + * 超时时间单位 + */ + final TimeUnit timeoutUnit; + + // ========== 临时属性 ========== + + /** + * worker将来要处理的param + */ + protected volatile T param; + /** + * 原子设置wrapper的状态 + *

+ * {@link State}此枚举类枚举了state值所代表的状态枚举。 + */ + protected final AtomicInteger state = new AtomicInteger(BUILDING.id); + /** + * 该值将在{@link IWorker#action(Object, Map)}进行时设为当前线程,在任务开始前或结束后都为null。 + */ + protected final AtomicReference doWorkingThread = new AtomicReference<>(); + /** + * 也是个钩子变量,用来存临时的结果 + */ + protected final AtomicReference> workResult = new AtomicReference<>(null); + + WorkerWrapper(String id, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit, + WrapperStrategy wrapperStrategy + ) { + if (worker == null) { + throw new NullPointerException("async.worker is null"); + } + this.worker = worker; + this.id = id; + //允许不设置回调 + if (callback == null) { + callback = new DefaultCallback<>(); + } + this.callback = callback; + this.allowInterrupt = allowInterrupt; + this.enableTimeout = enableTimeout; + this.timeoutLength = timeoutLength; + this.timeoutUnit = timeoutUnit; + this.wrapperStrategy = wrapperStrategy; + } + + WorkerWrapper(String id, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit) { + this(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, new StableWrapperStrategy()); + } + + // ========== public ========== + + /** + * 外部调用本线程运行此wrapper的入口方法。 + * 该方法将会确定这组wrapper所属的group。 + * + * @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。 + * @param remainTime 剩下的时间 + * @param group wrapper组 + * @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。 + */ + public void work(ExecutorService executorService, + long remainTime, + WorkerWrapperGroup group) { + work(executorService, null, remainTime, group); + } + + public String getId() { + return id; + } + + /** + * 返回{@link #workResult}的值。 + * 若调用此方法时workResult还未设置,将会返回{@link WorkResult#defaultResult()}。 + */ + public WorkResult getWorkResult() { + WorkResult res = workResult.get(); + return res == null ? WorkResult.defaultResult() : res; + } + + public void setParam(T param) { + this.param = param; + } + + public State getState() { + return of(state.get()); + } + + /** + * 获取下游Wrapper + */ + public abstract Set> getNextWrappers(); + + /** + * 获取上游wrapper + */ + public abstract Set> getDependWrappers(); + + /** + * 获取本wrapper的超时情况。如有必要还会修改wrapper状态。 + * + * @param withEndIt 如果为true,在检查出已经超时的时候,会将其快速结束。 + * @param startTime 起始时间 + * @param totalTimeLength 总任务时长 + * @return 超时返回-1L,结束但未超时返回0L,尚未结束且未超时返回与deadline的差值 + *

+ * 当没有超时,若该wrapper已经结束但没有超时,返回 0L 。 + *

+ * 如果该wrapper单独了设置超时策略并正在运行,返回距离超时策略限时相差的毫秒值。 + * 例如设置10ms超时,此时已经开始3ms,则返回 7L。 + * 如果此差值<1,则返回 1L。 + *

+ * 如果已经超时,则返回 -1L。 + *

+ */ + public long checkTimeout(boolean withEndIt, long startTime, long totalTimeLength) { + do { + WorkResult _workResult = workResult.get(); + // 如果已经有结果了,就根据结果值判断 + if (_workResult != null) { + return _workResult.getResultState() == ResultState.TIMEOUT ? -1L : 0L; + } + // 如果还没有出结果 + // 判断是否超时 + long now = SystemClock.now(); + if (totalTimeLength < now - startTime + || enableTimeout && timeoutUnit.toMillis(timeoutLength) < now - startTime) { + // 如果需要处理该wrapper的状态 + if (withEndIt) { + // CAS一个超时的结果 + if (!workResult.compareAndSet( + null, + new WorkResult<>(null, ResultState.TIMEOUT, null)) + ) { + // 就在想CAS的时候,出结果了,就采用新的结果重新判断一次 + continue; + } + fastFail(true, null, false); + } + return -1L; + } + // 正在运行,尚未超时 + else { + return Math.max(1, startTime + totalTimeLength - now); + } + } while (true); + } + + /** + * 直接取消wrapper运行。 + * 如果状态在 {@link State#states_of_beforeWorkingEnd}中,则调用 {@link #fastFail(boolean, Exception, boolean)}。 + */ + public void cancel() { + if (State.setState(state, states_of_beforeWorkingEnd, SKIP, null)) { + fastFail(false, new CancelSkippedException(), true); + } + } + + public WrapperStrategy getWrapperStrategy() { + return wrapperStrategy; + } + + // ========== protected ========== + + /** + * 工作的核心方法。 + * + * @param fromWrapper 代表这次work是由哪个上游wrapper发起的。如果是首个Wrapper则为null。 + * @param remainTime 剩余时间。 + * @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。 + */ + protected void work(ExecutorService executorService, + WorkerWrapper fromWrapper, + long remainTime, + WorkerWrapperGroup group + ) { + long now = SystemClock.now(); + // ================================================ + // 以下是一些lambda。 + // 因为抽取成方法反而不好传参、污染类方法,所以就这么干了 + final Consumer __function__callbackResult = + success -> { + WorkResult _workResult = getWorkResult(); + try { + callback.result(success, param, _workResult); + } catch (Exception e) { + if (setState(state, states_of_skipOrAfterWork, ERROR, null)) { + fastFail(false, e, _workResult.getEx() instanceof SkippedException); + } + } + }; + final Runnable __function__callbackResultOfFalse_beginNext = + () -> { + __function__callbackResult.accept(false); + beginNext(executorService, now, remainTime, group); + }; + final BiConsumer __function__fastFail_callbackResult$false_beginNext = + (fastFail_isTimeout, fastFail_exception) -> { + boolean isSkip = fastFail_exception instanceof SkippedException; + fastFail(fastFail_isTimeout && !isSkip, fastFail_exception, isSkip); + __function__callbackResultOfFalse_beginNext.run(); + }; + final Runnable __function__doWork = + () -> { + if (setState(state, STARTED, WORKING)) { + try { + fire(group); + } catch (Exception e) { + if (setState(state, WORKING, ERROR)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, e); + } + return; + } + } + if (setState(state, WORKING, AFTER_WORK)) { + __function__callbackResult.accept(true); + beginNext(executorService, now, remainTime, group); + } + }; + // ================================================ + // 开始执行 + try { + if (isState(state, BUILDING)) { + throw new IllegalStateException("wrapper can't work because state is BUILDING ! wrapper is " + this); + } + // 判断是否整组取消 + if (group.isWaitingCancel() || group.isCancelled()) { + cancel(); + return; + } + // 总的已经超时了,就快速失败,进行下一个 + if (remainTime <= 0) { + if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) { + __function__fastFail_callbackResult$false_beginNext.accept(true, null); + } + return; + } + // 如果自己已经执行过了。 + // 可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 + final AtomicReference oldStateRef = new AtomicReference<>(null); + if (!setState(state, states_of_notWorked, STARTED, oldStateRef::set)) { + return; + } + // 如果wrapper是第一次,要调用callback.begin + if (oldStateRef.get() == INIT) { + try { + callback.begin(); + } catch (Exception e) { + // callback.begin 发生异常 + if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, e); + } + return; + } + } + + //如果fromWrapper为null,说明自己就是第一批要执行的 + if (fromWrapper == null) { + // 首当其冲,开始工作 + __function__doWork.run(); + return; + } + + // 每个线程都需要判断是否要跳过自己,该方法可能会跳过正在工作的自己。 + final WrapperStrategy wrapperStrategy = getWrapperStrategy(); + if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) { + if (setState(state, STARTED, SKIP)) { + __function__fastFail_callbackResult$false_beginNext.accept(false, new SkippedException()); + } + return; + } + + // 如果是由其他wrapper调用而运行至此,则使用策略器决定自己的行为 + DependenceAction.WithProperty judge = + wrapperStrategy.judgeAction(getDependWrappers(), this, fromWrapper); + switch (judge.getDependenceAction()) { + case TAKE_REST: + return; + case FAST_FAIL: + if (setState(state, STARTED, ERROR)) { + // 根据FAST_FAIL.fastFailException()设置的属性值来设置fastFail方法的参数 + ResultState resultState = judge.getResultState(); + __function__fastFail_callbackResult$false_beginNext.accept( + resultState == ResultState.TIMEOUT, + judge.getFastFailException() + ); + } + return; + case START_WORK: + __function__doWork.run(); + return; + case JUDGE_BY_AFTER: + default: + throw new IllegalStateException( + "策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + + ",fromWrapper=" + fromWrapper); + } + } catch (Exception e) { + // wrapper本身抛出了不该有的异常 + setState(state, states_all, ERROR, null); + NotExpectedException ex = new NotExpectedException(e, this); + workResult.set(new WorkResult<>(null, ResultState.EXCEPTION, ex)); + __function__fastFail_callbackResult$false_beginNext.accept(false, ex); + } + } + + + /** + * 本工作线程执行自己的job. + *

+ * 本方法不负责校验状态。请在调用前自行检验 + */ + protected void fire(WorkerWrapperGroup group) { + try { + doWorkingThread.set(Thread.currentThread()); + //执行耗时操作 + V result = worker.action(param, group.getForParamUseWrappers()); + workResult.compareAndSet( + null, + new WorkResult<>(result, ResultState.SUCCESS) + ); + } finally { + doWorkingThread.set(null); + } + } + + /** + * 快速失败。 + * 该方法不负责检查状态,请自行控制。 + * + * @param isTimeout 是否是因为超时而快速失败 + * @param e 设置异常信息到{@link WorkResult#getEx()} + */ + protected void fastFail(boolean isTimeout, Exception e, boolean isSkip) { + // 试图打断正在执行{@link IWorker#action(Object, Map)}的线程 + Thread _doWorkingThread; + if ((_doWorkingThread = doWorkingThread.get()) != null + // 不会打断自己 + && !Objects.equals(Thread.currentThread(), _doWorkingThread)) { + _doWorkingThread.interrupt(); + } + // 尚未处理过结果则设置 + workResult.compareAndSet(null, new WorkResult<>( + worker.defaultValue(), + isTimeout ? ResultState.TIMEOUT : (isSkip ? ResultState.DEFAULT : ResultState.EXCEPTION), + e + )); + } + + /** + * 进行下一个任务 + *

+ * 本方法不负责校验状态。请在调用前自行检验 + */ + protected void beginNext(ExecutorService executorService, long now, long remainTime, WorkerWrapperGroup group) { + //花费的时间 + final long costTime = SystemClock.now() - now; + final long nextRemainTIme = remainTime - costTime; + Set> nextWrappers = getNextWrappers(); + if (nextWrappers == null) { + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); + return; + } + // nextWrappers只有一个,就用本线程继续跑。 + if (nextWrappers.size() == 1) { + WorkerWrapper next = null; + try { + next = nextWrappers.stream().findFirst().get(); + group.addWrapper(next); + setState(state, AFTER_WORK, SUCCESS); + } finally { + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); + if (next != null) { + next.work(executorService, this, nextRemainTIme, group); + } + } + } + // nextWrappers有多个 + else { + try { + group.addWrapper(nextWrappers); + nextWrappers.forEach(next -> executorService.submit(() -> + next.work(executorService, this, nextRemainTIme, group)) + ); + setState(state, AFTER_WORK, SUCCESS); + } finally { + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); + } + } + + } + + // ========== hashcode and equals ========== + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + /** + * {@code return id.hashCode();}返回id值的hashcode + */ + @Override + public int hashCode() { + // final String id can use to .hashcode() . + return id.hashCode(); + } + + // ========== builder ========== + + public static WorkerWrapperBuilder builder() { + return new Builder<>(); + } + + /** + * 自v1.5,该类被抽取到{@link StableWorkerWrapperBuilder}抽象类,兼容之前的版本。 + */ + public static class Builder extends StableWorkerWrapperBuilder> { + /** + * @deprecated 建议使用 {@link #builder()}返回{@link WorkerWrapperBuilder}接口,以调用v1.5之后的规范api + */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + public Builder() { + } + } + + // ========== package access methods ========== + + abstract void setNextWrappers(Set> nextWrappers); + + abstract void setDependWrappers(Set> dependWrappers); + + // ========== toString ========== + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(400) + .append("WorkerWrapper{id=").append(id) + .append(", state=").append(of(state.get())) + .append(", param=").append(param) + .append(", workResult=").append(workResult) + .append(", allowInterrupt=").append(allowInterrupt) + .append(", enableTimeout=").append(enableTimeout) + .append(", timeoutLength=").append(timeoutLength) + .append(", timeoutUnit=").append(timeoutUnit) + // 防止循环引用,这里只输出相关Wrapper的id + .append(", dependWrappers::getId=["); + final Set> dependWrappers = getDependWrappers(); + dependWrappers.stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", ")); + if (dependWrappers.size() > 0) { + sb.delete(sb.length() - 2, sb.length()); + } + sb + .append("], nextWrappers::getId=["); + final Set> nextWrappers = getNextWrappers(); + nextWrappers.stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", ")); + if (nextWrappers.size() > 0) { + sb.delete(sb.length() - 2, sb.length()); + } + sb + .append("], doWorkingThread=").append(doWorkingThread.get()) + .append(", worker=").append(worker) + .append(", callback=").append(callback) + .append(", wrapperStrategy=").append(wrapperStrategy) + .append('}'); + return sb.toString(); + } + + /** + * 一个通用的策略器实现类,提供了修改的功能。并兼容之前的代码。 + */ + public static class StableWrapperStrategy extends WrapperStrategy.AbstractWrapperStrategy { + private DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper; + private DependMustStrategyMapper dependMustStrategyMapper; + private DependenceStrategy dependenceStrategy; + private SkipStrategy skipStrategy; + + @Override + public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() { + return dependOnUpWrapperStrategyMapper; + } + + @Override + public void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) { + this.dependOnUpWrapperStrategyMapper = dependOnUpWrapperStrategyMapper; + } + + @Override + public DependMustStrategyMapper getDependMustStrategyMapper() { + return dependMustStrategyMapper; + } + + @Override + public void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) { + this.dependMustStrategyMapper = dependMustStrategyMapper; + } + + @Override + public DependenceStrategy getDependenceStrategy() { + return dependenceStrategy; + } + + @Override + public void setDependenceStrategy(DependenceStrategy dependenceStrategy) { + this.dependenceStrategy = dependenceStrategy; + } + + @Override + public SkipStrategy getSkipStrategy() { + return skipStrategy; + } + + @Override + public void setSkipStrategy(SkipStrategy skipStrategy) { + this.skipStrategy = skipStrategy; + } + } + + /** + * state状态枚举工具类 + */ + public enum State { + /** + * 初始化中,builder正在设置其数值 + */ + BUILDING(-1), + /** + * 初始化完成,但是还未执行过。 + */ + INIT(0), + /** + * 执行过。 + * 即至少进行了一次各种判定,例如判断 是否跳过/是否启动工作 + */ + STARTED(1), + /** + * 工作状态 + */ + WORKING(2), + /** + * 工作完成后的收尾工作,例如调用下游wrapper + */ + AFTER_WORK(3), + /** + * wrapper成功执行结束 + */ + SUCCESS(4), + /** + * wrapper失败了 + */ + ERROR(5), + /** + * wrapper被跳过 + */ + SKIP(6); + + // public + + public boolean finished() { + return this == SUCCESS || this == ERROR || this == SKIP; + } + + // package + + State(int id) { + this.id = id; + } + + final int id; + + // package-static + + static final State[] states_of_notWorked = new State[]{INIT, STARTED}; + + static final State[] states_of_skipOrAfterWork = new State[]{SKIP, AFTER_WORK}; + + static final State[] states_of_beforeWorkingEnd = new State[]{INIT, STARTED, WORKING}; + + static final State[] states_all = new State[]{BUILDING, INIT, STARTED, WORKING, AFTER_WORK, SUCCESS, ERROR, SKIP}; + + /** + * 自旋+CAS的设置状态,如果状态不在exceptValues返回内 或 没有设置成功,则返回false。 + * + * @param state {@link WorkerWrapper#state} 要被修改的AtomicInteger引用 + * @param exceptValues 期望的值数组,任何满足该值的state都会被修改 + * @param newValue 新值 + * @param withOperate 如果该参数不为null并且成功设置,该函数将会被执行,其参数为wrapper原子设置之前的旧状态。 + * 之所以需要这个参数,是因为当except值有多个时,无法确定是哪个值被原子修改了。 + * @return 返回是否成功设置。 + */ + static boolean setState(AtomicInteger state, + State[] exceptValues, + State newValue, + Consumer withOperate) { + int current; + boolean inExcepts; + while (true) { + // 判断当前值是否在exceptValues范围内 + current = state.get(); + inExcepts = false; + for (State exceptValue : exceptValues) { + if (inExcepts = current == exceptValue.id) { + break; + } + } + // 如果不在 exceptValues 范围内,直接返回false。 + if (!inExcepts) { + return false; + } + // 如果在 exceptValues 范围,cas成功返回true,失败(即当前值被修改)则自旋。 + if (state.compareAndSet(current, newValue.id)) { + if (withOperate != null) { + withOperate.accept(of(current)); + } + return true; + } + } + } + + /** + * 自旋+CAS的设置状态,如果状态不在exceptValues返回内 或 没有设置成功自旋后不在范围内,则返回false。 + * + * @param state {@link WorkerWrapper#state} 要被修改的AtomicInteger引用 + * @param exceptValue 期望的值 + * @param newValue 新值 + * @return 返回是否成功设置。 + */ + static boolean setState(AtomicInteger state, + State exceptValue, + State newValue) { + int current; + // 如果当前值与期望值相同 + while ((current = state.get()) == exceptValue.id) { + // 则尝试CAS设置新值 + if (state.compareAndSet(current, newValue.id)) { + return true; + } + // 如果当前值被改变,则尝试自旋 + } + // 如果当前值与期望值不相同了,就直接返回false + return false; + } + + /** + * 自旋+CAS的判断是否在这些excepts范围内 + * + * @param excepts 范围。 + */ + @SuppressWarnings("unused") + static boolean inStates(AtomicInteger state, State... excepts) { + int current; + boolean inExcepts; + while (true) { + current = state.get(); + inExcepts = false; + for (State except : excepts) { + if (current == except.id) { + inExcepts = true; + break; + } + } + if (state.get() == current) { + return inExcepts; + } + } + } + + /** + * CAS的判断是否是某个状态 + */ + static boolean isState(AtomicInteger state, @SuppressWarnings("SameParameterValue") State except) { + return state.compareAndSet(except.id, except.id); + } + + static State of(int id) { + return id2state.get(id); + } + + static final Map id2state; + + static { + HashMap map = new HashMap<>(); + for (State s : State.values()) { + map.put(s.id, s); + } + id2state = Collections.unmodifiableMap(map); + } + + + } + + /** + * 这是因未知错误而引发的异常 + */ + public static class NotExpectedException extends Exception { + public NotExpectedException(Throwable cause, WorkerWrapper wrapper) { + super("It's should not happened Exception . wrapper is " + wrapper, cause); + } + } +} diff --git a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java similarity index 89% rename from src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java index 4a519c0..e5307d9 100644 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java @@ -2,10 +2,9 @@ package com.jd.platform.async.wrapper; import com.jd.platform.async.callback.ICallback; import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.worker.WorkResult; -import com.jd.platform.async.wrapper.actionstrategy.DependWrapperActionStrategy; -import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy; -import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; import java.util.Collection; import java.util.concurrent.TimeUnit; @@ -113,9 +112,11 @@ public interface WorkerWrapperBuilder { * @param wrapper 需要设置特殊策略的Wrapper。 * @param strategy 特殊策略。 */ - SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); + @SuppressWarnings("UnusedReturnValue") + SetDepend specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper); - default SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper... wrappers) { + @SuppressWarnings("unused") + default SetDepend specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper... wrappers) { if (strategy == null || wrappers == null) { return this; } @@ -157,6 +158,7 @@ public interface WorkerWrapperBuilder { return setDepend().wrapper(wrappers).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder depends(Collection wrappers) { return setDepend().wrapper(wrappers).end(); } @@ -165,6 +167,7 @@ public interface WorkerWrapperBuilder { return setDepend().wrapper(wrappers).strategy(strategy).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder depends(DependenceStrategy strategy, Collection wrappers) { return setDepend().wrapper(wrappers).strategy(strategy).end(); } @@ -205,6 +208,7 @@ public interface WorkerWrapperBuilder { */ SetNext mustToNextWrapper(WorkerWrapper wrapper); + @SuppressWarnings("unused") default SetNext requireToNextWrapper(WorkerWrapper wrapper, boolean must) { return must ? mustToNextWrapper(wrapper) : wrapper(wrapper); } @@ -216,7 +220,7 @@ public interface WorkerWrapperBuilder { * @param wrapper 依赖本Wrapper的下游Wrapper。 * @return 返回Builder自身。 */ - SetNext specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); + SetNext specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper wrapper); WorkerWrapperBuilder end(); } @@ -230,6 +234,7 @@ public interface WorkerWrapperBuilder { return setNext().wrapper(wrappers).end(); } + @SuppressWarnings("unused") default WorkerWrapperBuilder nextOf(Collection wrappers) { return setNext().wrapper(wrappers).end(); } @@ -257,13 +262,6 @@ public interface WorkerWrapperBuilder { */ SetTimeOut setTime(long time, TimeUnit unit); - /** - * 是否允许被试图中断线程 - * - * @param allow 是则true - */ - SetTimeOut allowInterrupt(boolean allow); - WorkerWrapperBuilder end(); } @@ -274,13 +272,20 @@ public interface WorkerWrapperBuilder { * @param unit 时间单位 */ default WorkerWrapperBuilder timeout(long time, TimeUnit unit) { - return timeout(true, time, unit, false); + return timeout(true, time, unit); } - default WorkerWrapperBuilder timeout(boolean enableTimeOut, long time, TimeUnit unit, boolean allowInterrupt) { - return setTimeOut().enableTimeOut(enableTimeOut).setTime(time, unit).allowInterrupt(allowInterrupt).end(); + default WorkerWrapperBuilder timeout(boolean enableTimeOut, long time, TimeUnit unit) { + return setTimeOut().enableTimeOut(enableTimeOut).setTime(time, unit).end(); } + /** + * 是否允许被试图中断线程 + * + * @param allow 是则true + */ + WorkerWrapperBuilder allowInterrupt(boolean allow); + /** * 构建Wrapper。 * diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java new file mode 100644 index 0000000..5ae630b --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java @@ -0,0 +1,212 @@ +package com.jd.platform.async.wrapper; + +import com.jd.platform.async.exception.CancelSkippedException; +import com.jd.platform.async.executor.PollingCenter; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +import com.jd.platform.async.executor.timer.SystemClock; +import com.jd.platform.async.openutil.timer.*; + +/** + * @author create by TcSnZh on 2021/5/9-下午7:21 + */ +public class WorkerWrapperGroup { + /** + * 任务开始时间 + */ + private final long groupStartTime; + /** + * 任务限时 + */ + private final long timeoutLength; + /** + * 该map存放所有wrapper的id和wrapper映射 + *

+ * 需要线程安全。 + */ + private final Map> forParamUseWrappers = new ConcurrentHashMap<>(); + /** + * 当全部wrapper都调用结束,它会countDown + */ + private final CountDownLatch endCDL = new CountDownLatch(1); + /** + * 检测到超时,此标记变量将为true。 + */ + private final AtomicBoolean anyTimeout = new AtomicBoolean(false); + /** + * 结束时间 + */ + private volatile long finishTime = -1L; + /** + * 取消任务状态 + * 0 - not cancel , 1 - waiting cancel , 2 - already cancel + */ + private final AtomicInteger cancelState = new AtomicInteger(); + + public static final int NOT_CANCEL = 0; + public static final int WAITING_CANCEL = 1; + public static final int ALREADY_CANCEL = 2; + + public WorkerWrapperGroup(long groupStartTime, long timeoutLength) { + this.groupStartTime = groupStartTime; + this.timeoutLength = timeoutLength; + } + + public void addWrapper(Collection> wrapper) { + Objects.requireNonNull(wrapper).forEach(this::addWrapper); + } + + public void addWrapper(WorkerWrapper... wrappers) { + for (WorkerWrapper wrapper : Objects.requireNonNull(wrappers)) { + addWrapper(wrapper); + } + } + + public void addWrapper(WorkerWrapper wrapper) { + if (wrapper != null) { + forParamUseWrappers.put(wrapper.id, wrapper); + } + } + + public Map> getForParamUseWrappers() { + return forParamUseWrappers; + } + + public CountDownLatch getEndCDL() { + return endCDL; + } + + public long getGroupStartTime() { + return groupStartTime; + } + + public AtomicBoolean getAnyTimeout() { + return anyTimeout; + } + + public long getFinishTime() { + return finishTime; + } + + public boolean isCancelled() { + return cancelState.get() == ALREADY_CANCEL; + } + + public boolean isWaitingCancel() { + return cancelState.get() == WAITING_CANCEL; + } + + @SuppressWarnings("UnusedReturnValue") + public boolean pleaseCancel() { + return cancelState.compareAndSet(NOT_CANCEL, WAITING_CANCEL); + } + + public class CheckFinishTask implements TimerTask { + + @SuppressWarnings("RedundantThrows") + @Override + public void run(Timeout timeout) throws Exception { + // 已经完成了 + if (endCDL.getCount() < 1) { + return; + } + AtomicBoolean hasTimeout = new AtomicBoolean(false); + // 记录正在运行中的wrapper里,最近的限时时间。 + final AtomicLong minDaley = new AtomicLong(Long.MAX_VALUE); + final Collection> values = forParamUseWrappers.values(); + final Stream> stream = values.size() > 128 ? values.parallelStream() : values.stream(); + final boolean needCancel = cancelState.get() == WAITING_CANCEL; + boolean allFinish_and_notNeedCancel = stream + // 需要取消的话就取消 + .peek(wrapper -> { + if (needCancel) { + wrapper.cancel(); + } + }) + // 检查超时并保存最近一次限时时间 + // 当需要取消时,才会不断遍历。如果不需要取消,则计算一次(或并行流中多次)就因allMatch不满足而退出了。 + .peek(wrapper -> { + // time_diff : + // -1 -> already timeout ; + // 0 -> finish but not timeout ; + // X>0 -> is running , may timeout in X seconds . + long time_diff = wrapper.checkTimeout(true, groupStartTime, timeoutLength); + if (time_diff < 0) { + hasTimeout.set(true); + } + if (time_diff == 0) { + return; + } + // use CAS and SPIN for thread safety in parallelStream . + do { + long getMinDaley = minDaley.get(); + // 需要设置最小时间,但是cas失败,则自旋 + if (getMinDaley <= time_diff && !minDaley.compareAndSet(getMinDaley, time_diff)) { + continue; + } + return; + } while (true); + }) + // 判断是否不需要取消且全部结束 + // 在不需要取消时,这里如果还有未结束的wrapper则会提前结束流并返回false + // 在需要取消时,会全部遍历一遍并取消掉已经进入链路的wrapper + .allMatch(wrapper -> !needCancel && wrapper.getState().finished()); + long getMinDaley = minDaley.get(); + // 如果本次取消掉了任务,或是所有wrapper都已经完成 + // ( ps : 前后两条件在这里是必定 一真一假 或 两者全假 ) + if (needCancel || allFinish_and_notNeedCancel) { + // 如果这次进行了取消,则设置取消状态为已完成 + if (needCancel) { + cancelState.set(ALREADY_CANCEL); + } + anyTimeout.set(hasTimeout.get()); + finishTime = SystemClock.now(); + endCDL.countDown(); + } + // 如果有正在运行的wrapper + else { + // 如果有正在WORKING的wrapper,则计算一下限时时间,限时完成后轮询它。 + if (getMinDaley != Long.MAX_VALUE) { + PollingCenter.getInstance().checkGroup(this, getMinDaley); + } + } + } + + // hashCode and equals will called WorkerWrapperGroup.this + + /** + * 将会调用{@link WorkerWrapperGroup#hashCode()} + */ + @Override + public int hashCode() { + return WorkerWrapperGroup.this.hashCode(); + } + + /** + * 将会调用{@link WorkerWrapperGroup#equals(Object)} + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof CheckFinishTask)) { + return false; + } + return Objects.equals(WorkerWrapperGroup.this, ((CheckFinishTask) obj).getParent()); + } + + private WorkerWrapperGroup getParent() { + return WorkerWrapperGroup.this; + } + } +} diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java new file mode 100644 index 0000000..044c8d9 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/WrapperStrategy.java @@ -0,0 +1,135 @@ +package com.jd.platform.async.wrapper.strategy; + +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; + +import java.util.Set; + +/** + * @author create by TcSnZh on 2021/5/17-下午6:23 + */ +public interface WrapperStrategy extends DependenceStrategy, SkipStrategy { + // ========== 这三个策略器用于链式判断是否要开始工作 ========== + + // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy + + /** + * 设置对特殊Wrapper专用的依赖响应策略。 + * + * @return 该值允许为null + */ + DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper(); + + /** + * 对必须完成的(must的)Wrapper的依赖响应策略。 + * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 + * + * @return 该值允许为null + * @deprecated 不推荐使用,很有可能被遗弃 + */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + default DependMustStrategyMapper getDependMustStrategyMapper() { + return null; + } + + /** + * 底层全局策略。 + * + * @return 该值不允许为null + */ + DependenceStrategy getDependenceStrategy(); + + // ========== 这是跳过策略 ========== + + /** + * 跳过策略 + * + * @return 不允许为null + */ + SkipStrategy getSkipStrategy(); + + @Override + default DependenceAction.WithProperty judgeAction(Set> dependWrappers, + WorkerWrapper thisWrapper, + WorkerWrapper fromWrapper) { + // 如果存在依赖,则调用三层依赖响应策略进行判断 + + DependenceStrategy strategy = getDependWrapperStrategyMapper(); + if (getDependMustStrategyMapper() != null) { + strategy = strategy == null ? getDependMustStrategyMapper() : strategy.thenJudge(getDependenceStrategy()); + } + if (getDependenceStrategy() != null) { + strategy = strategy == null ? getDependenceStrategy() : strategy.thenJudge(getDependenceStrategy()); + } + if (strategy == null) { + throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!"); + } + return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper); + } + + @Override + default boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return getSkipStrategy() != null && getSkipStrategy().shouldSkip(nextWrappers, thisWrapper, fromWrapper); + } + + default void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) { + throw new UnsupportedOperationException(); + } + + default void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) { + throw new UnsupportedOperationException(); + } + + default void setDependenceStrategy(DependenceStrategy dependenceStrategy) { + throw new UnsupportedOperationException(); + } + + default void setSkipStrategy(SkipStrategy skipStrategy) { + throw new UnsupportedOperationException(); + } + + /** + * 抽象策略器,实现了toString + */ + abstract class AbstractWrapperStrategy implements WrapperStrategy { + @Override + public String toString() { + return "WrapperStrategy{" + + "dependWrapperStrategyMapper=" + getDependWrapperStrategyMapper() + + ", dependMustStrategyMapper=" + getDependMustStrategyMapper() + + ", dependenceStrategy=" + getDependenceStrategy() + + ", skipStrategy=" + getSkipStrategy() + + '}'; + } + } + + /** + * 默认策略器,用默认值实现了所有属性。 + */ + class DefaultWrapperStrategy extends AbstractWrapperStrategy { + @Override + public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() { + return null; + } + + @Override + public DependMustStrategyMapper getDependMustStrategyMapper() { + return null; + } + + @Override + public DependenceStrategy getDependenceStrategy() { + return DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS; + } + + @Override + public SkipStrategy getSkipStrategy() { + return SkipStrategy.CHECK_ONE_LEVEL; + } + } +} diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java similarity index 95% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java index 8ad1019..b4b1a24 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.wrapper.actionstrategy; +package com.jd.platform.async.wrapper.strategy.depend; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.wrapper.WorkerWrapper; @@ -26,7 +26,7 @@ public class DependMustStrategyMapper implements DependenceStrategy { * 如果所有的Wrapper已经完成,本Wrapper将会开始工作。 *

* 如果任一{@link #mustDependSet}中的Wrapper失败,则返回{@link DependenceAction#FAST_FAIL}。 - * 具体超时/异常则根据{@link com.jd.platform.async.worker.ResultState}的值进行判断。 + * 具体超时/异常则根据{@link ResultState}的值进行判断。 *

* 如果存在Wrapper未完成 且 所有的Wrapper都未失败,则返回{@link DependenceAction#JUDGE_BY_AFTER}。 *

diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java similarity index 85% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java index 745168f..a5f0d03 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategy.java @@ -1,16 +1,16 @@ -package com.jd.platform.async.wrapper.actionstrategy; +package com.jd.platform.async.wrapper.strategy.depend; import com.jd.platform.async.wrapper.WorkerWrapper; /** - * 单参数策略。 + * 由上游wrapper决定本wrapper行为的单参数策略。 * * @author create by TcSnZh on 2021/5/1-下午11:16 */ @FunctionalInterface -public interface DependWrapperActionStrategy { +public interface DependOnUpWrapperStrategy { /** - * 仅使用一个参数的判断方法 + * 仅使用一个参数(即调用自身的上游wrapper)的判断方法 * * @param fromWrapper 调用本Wrapper的上游Wrapper * @return 返回 {@link DependenceAction.WithProperty} @@ -24,7 +24,7 @@ public interface DependWrapperActionStrategy { * 未运行时,休息。 * 失败时,失败。 */ - DependWrapperActionStrategy SUCCESS_CONTINUE = new DependWrapperActionStrategy() { + DependOnUpWrapperStrategy SUCCESS_CONTINUE = new DependOnUpWrapperStrategy() { @Override public DependenceAction.WithProperty judge(WorkerWrapper ww) { switch (ww.getWorkResult().getResultState()) { @@ -50,7 +50,7 @@ public interface DependWrapperActionStrategy { * 未运行时,交给下一个策略器判断。 * 失败时,失败。 */ - DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE = new DependWrapperActionStrategy() { + DependOnUpWrapperStrategy SUCCESS_START_INIT_CONTINUE = new DependOnUpWrapperStrategy() { @Override public DependenceAction.WithProperty judge(WorkerWrapper ww) { switch (ww.getWorkResult().getResultState()) { diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java similarity index 75% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java index 5c2cd56..476aa05 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependOnUpWrapperStrategyMapper.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.wrapper.actionstrategy; +package com.jd.platform.async.wrapper.strategy.depend; import com.jd.platform.async.wrapper.WorkerWrapper; @@ -10,12 +10,12 @@ import java.util.stream.Collectors; /** * 对不同的{@link WorkerWrapper}调用者实行个性化依赖响应策略。 *

- * 使用{@link DependWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强, + * 使用{@link DependOnUpWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强, * * @author create by TcSnZh on 2021/5/1-下午11:12 */ -public class DependWrapperStrategyMapper implements DependenceStrategy { - private final Map, DependWrapperActionStrategy> mapper = new ConcurrentHashMap<>(4); +public class DependOnUpWrapperStrategyMapper implements DependenceStrategy { + private final Map, DependOnUpWrapperStrategy> mapper = new ConcurrentHashMap<>(4); /** * 设置对应策略 @@ -24,7 +24,8 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { * @param strategy 要设置的策略 * @return 返回this,链式调用。 */ - public DependWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependWrapperActionStrategy strategy) { + @SuppressWarnings("UnusedReturnValue") + public DependOnUpWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependOnUpWrapperStrategy strategy) { mapper.put(targetWrapper, strategy); toStringCache = null; return this; @@ -33,7 +34,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { /** * 判断方法。 *

- * 如果fromWrapper在{@link #mapper}中,则返回{@link DependWrapperActionStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER} + * 如果fromWrapper在{@link #mapper}中,则返回{@link DependOnUpWrapperStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER} * * @param dependWrappers (这里不会使用该值)thisWrapper.dependWrappers的属性值。 * @param thisWrapper (这里不会使用该值)thisWrapper,即为“被催促”的WorkerWrapper @@ -44,7 +45,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy { public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - DependWrapperActionStrategy strategy = mapper.get(fromWrapper); + DependOnUpWrapperStrategy strategy = mapper.get(fromWrapper); if (strategy == null) { return DependenceAction.JUDGE_BY_AFTER.emptyProperty(); } diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceAction.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java similarity index 80% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceAction.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java index acbf5d5..3e21257 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceAction.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceAction.java @@ -1,4 +1,4 @@ -package com.jd.platform.async.wrapper.actionstrategy; +package com.jd.platform.async.wrapper.strategy.depend; import com.jd.platform.async.worker.ResultState; @@ -13,7 +13,7 @@ public enum DependenceAction { */ START_WORK, /** - * 还没轮到,休息一下。WorkerWrapper中的调用栈会返回,以等待可能发生的下次调用。 + * 还没轮到,休息一下。WorkerWrapper中的调用栈会返回,以等待其他上游wrapper调用它,或是会一生无缘被调用。 */ TAKE_REST, /** @@ -22,6 +22,8 @@ public enum DependenceAction { FAST_FAIL, /** * 交给下层{@link DependenceStrategy}进行判断。 + * 由于{@link DependenceStrategy#thenJudge(DependenceStrategy)}的责任链设计模式,该返回值的意义就是调用责任链上下一个策略。 + *

* 在WorkerWrapper中不需要考虑此值,因为配置正常的情况下不会返回这个值。 */ JUDGE_BY_AFTER; @@ -29,6 +31,10 @@ public enum DependenceAction { // 空值单例 public WithProperty emptyProperty() { + if (this == FAST_FAIL) { + throw new UnsupportedOperationException( + "配置错误: FAST_FAIL 不能使用该方法,请使用fastFailException(ResultState, Exception)具体设置fastFail的参数。"); + } return empty; } @@ -66,6 +72,9 @@ public enum DependenceAction { * 所有的构造方法权限均为private,请在父枚举类{@link DependenceAction}的方法中选择合适的模板生成内部类WithProperty。 */ public class WithProperty { + /** + * 以下两个属性用于设置fastFail的属性 + */ private ResultState resultState; private Exception fastFailException; diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java similarity index 89% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceStrategy.java rename to asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java index 56368ea..a075369 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceStrategy.java +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java @@ -1,11 +1,13 @@ -package com.jd.platform.async.wrapper.actionstrategy; +package com.jd.platform.async.wrapper.strategy.depend; -import com.jd.platform.async.wrapper.WrapperEndingInspector; +import com.jd.platform.async.exception.SkippedException; import com.jd.platform.async.worker.ResultState; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; -import java.util.*; +import java.util.Collections; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; @@ -36,12 +38,12 @@ public interface DependenceStrategy { * @param fromWrapper 调用来源Wrapper。 *

* 该参数不会为null。 - * 因为在{@link WorkerWrapper#work(ExecutorService, long, Map, WrapperEndingInspector)}方法中传入的的第一批无依赖的Wrapper, + * 因为在{@link WorkerWrapper#work(ExecutorService, long, WorkerWrapperGroup)}方法中传入的的第一批无依赖的Wrapper, * 不会被该策略器所判断,而是不论如何直接执行。 *

* @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty} */ - DependenceAction.WithProperty judgeAction(Set> dependWrappers, + DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper); @@ -55,7 +57,7 @@ public interface DependenceStrategy { DependenceStrategy that = this; return new DependenceStrategy() { @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { DependenceAction.WithProperty judge = that.judgeAction(dependWrappers, thisWrapper, fromWrapper); @@ -81,7 +83,7 @@ public interface DependenceStrategy { */ DependenceStrategy ALL_DEPENDENCIES_ALL_SUCCESS = new DependenceStrategy() { @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { boolean hasWaiting = false; @@ -119,7 +121,7 @@ public interface DependenceStrategy { */ DependenceStrategy ALL_DEPENDENCIES_ANY_SUCCESS = new DependenceStrategy() { @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { boolean hasFailed = false; @@ -136,6 +138,7 @@ public interface DependenceStrategy { case EXCEPTION: resultState = !hasFailed ? workResult.getResultState() : resultState; fastFailException = !hasFailed ? workResult.getEx() : fastFailException; + // 跳过不算失败 hasFailed = true; break; default: @@ -155,11 +158,13 @@ public interface DependenceStrategy { }; /** - * 如果被依赖的工作中任一失败,则立即失败。否则就开始工作(不论之前的工作有没有开始)。 + * 如果被依赖的工作中任一失败,则立即失败。 + * 否则就开始工作(不论之前的工作有没有开始)。 */ + @SuppressWarnings("unused") DependenceStrategy ALL_DEPENDENCIES_NONE_FAILED = new DependenceStrategy() { @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { for (WorkerWrapper dependWrapper : dependWrappers) { @@ -167,7 +172,7 @@ public interface DependenceStrategy { switch (workResult.getResultState()) { case TIMEOUT: case EXCEPTION: - return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx()); + return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx()); default: } } @@ -188,7 +193,8 @@ public interface DependenceStrategy { * @param theseWrapper 该方法唯一有效参数。 * @return 返回生成的 {@link DependenceAction.WithProperty) */ - static DependenceStrategy theseWrapperAllSuccess(Set> theseWrapper) { + @SuppressWarnings("unused") + static DependenceStrategy theseWrapperAllSuccess(Set> theseWrapper) { return new DependenceStrategy() { private final Set> theseWrappers; private final String toString; @@ -199,7 +205,7 @@ public interface DependenceStrategy { } @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { boolean hasWaiting = false; @@ -232,7 +238,17 @@ public interface DependenceStrategy { }; } - DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() { + /** + * 此值用于适配v1.4及之前的must开关模式, + * 当`wrapperStrategy`的`dependMustStrategyMapper`的`mustDependSet`不为空时, + * 则休息(因为能判断到这个责任链说明set中存在不满足的值L)。 + * 为空时,则任一成功则执行。 + * + * @deprecated 不推荐使用must开关 + */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() { @Override public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, diff --git a/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java new file mode 100644 index 0000000..1b4f981 --- /dev/null +++ b/asyncTool-core/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java @@ -0,0 +1,53 @@ +package com.jd.platform.async.wrapper.strategy.skip; + +import com.jd.platform.async.wrapper.WorkerWrapper; + +import java.util.Set; + +/** + * @author create by TcSnZh on 2021/5/6-下午3:02 + */ +@FunctionalInterface +public interface SkipStrategy { + /** + * 跳过策略函数。返回true将会使WorkerWrapper跳过执行。 + * + * @param nextWrappers 下游WrapperSet + * @param thisWrapper 本WorkerWrapper + * @param fromWrapper 呼叫本Wrapper的上游Wrapper + * @return 返回true将会使WorkerWrapper跳过执行。 + */ + boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper); + + /** + * 不跳过 + */ + SkipStrategy NOT_SKIP = new SkipStrategy() { + @Override + public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return false; + } + + @Override + public String toString() { + return "NOT_SKIP"; + } + }; + + /** + * 距离为1的wrapper都不在初始化状态 + */ + SkipStrategy CHECK_ONE_LEVEL = new SkipStrategy() { + + @Override + public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return nextWrappers != null && !nextWrappers.isEmpty() + && nextWrappers.stream().allMatch(workerWrapper -> workerWrapper.getState().finished()); + } + + @Override + public String toString() { + return "CHECK_ONE_LEVEL"; + } + }; +} diff --git a/src/test/java/beforev14/depend/DeWorker.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker.java similarity index 92% rename from src/test/java/beforev14/depend/DeWorker.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker.java index e9f2b82..725ab84 100755 --- a/src/test/java/beforev14/depend/DeWorker.java +++ b/asyncTool-core/src/test/java/beforev14/depend/DeWorker.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker implements IWorker, ICallback { @Override - public User action(String object, Map allWrappers) { + public User action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/depend/DeWorker1.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker1.java similarity index 97% rename from src/test/java/beforev14/depend/DeWorker1.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker1.java index b958768..38bfbf6 100755 --- a/src/test/java/beforev14/depend/DeWorker1.java +++ b/asyncTool-core/src/test/java/beforev14/depend/DeWorker1.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker1 implements IWorker, User>, ICallback, User> { @Override - public User action(WorkResult result, Map allWrappers) { + public User action(WorkResult result, Map> allWrappers) { System.out.println("par1的入参来自于par0: " + result.getResult()); try { Thread.sleep(1000); diff --git a/src/test/java/beforev14/depend/DeWorker2.java b/asyncTool-core/src/test/java/beforev14/depend/DeWorker2.java similarity index 97% rename from src/test/java/beforev14/depend/DeWorker2.java rename to asyncTool-core/src/test/java/beforev14/depend/DeWorker2.java index c2a48c4..3567643 100755 --- a/src/test/java/beforev14/depend/DeWorker2.java +++ b/asyncTool-core/src/test/java/beforev14/depend/DeWorker2.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker2 implements IWorker, String>, ICallback, String> { @Override - public String action(WorkResult result, Map allWrappers) { + public String action(WorkResult result, Map> allWrappers) { System.out.println("par2的入参来自于par1: " + result.getResult()); try { Thread.sleep(1000); diff --git a/src/test/java/beforev14/depend/LambdaTest.java b/asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java similarity index 95% rename from src/test/java/beforev14/depend/LambdaTest.java rename to asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java index 93037a5..7dbaeca 100644 --- a/src/test/java/beforev14/depend/LambdaTest.java +++ b/asyncTool-core/src/test/java/beforev14/depend/LambdaTest.java @@ -10,10 +10,11 @@ import com.jd.platform.async.wrapper.WorkerWrapper; * @author sjsdfg * @since 2020/6/14 */ +@SuppressWarnings({"deprecation", "RedundantStringFormatCall"}) class LambdaTest { public static void main(String[] args) throws Exception { WorkerWrapper, String> workerWrapper2 = new WorkerWrapper.Builder, String>() - .worker((WorkResult result, Map allWrappers) -> { + .worker((WorkResult result, Map> allWrappers) -> { System.out.println("par2的入参来自于par1: " + result.getResult()); try { Thread.sleep(1000); @@ -28,7 +29,7 @@ class LambdaTest { .build(); WorkerWrapper, User> workerWrapper1 = new WorkerWrapper.Builder, User>() - .worker((WorkResult result, Map allWrappers) -> { + .worker((WorkResult result, Map> allWrappers) -> { System.out.println("par1的入参来自于par0: " + result.getResult()); try { Thread.sleep(1000); @@ -44,7 +45,7 @@ class LambdaTest { .build(); WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker((String object, Map allWrappers) -> { + .worker((String object, Map> allWrappers) -> { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/depend/Test.java b/asyncTool-core/src/test/java/beforev14/depend/Test.java similarity index 100% rename from src/test/java/beforev14/depend/Test.java rename to asyncTool-core/src/test/java/beforev14/depend/Test.java diff --git a/src/test/java/beforev14/depend/User.java b/asyncTool-core/src/test/java/beforev14/depend/User.java similarity index 100% rename from src/test/java/beforev14/depend/User.java rename to asyncTool-core/src/test/java/beforev14/depend/User.java diff --git a/src/test/java/beforev14/dependnew/DeWorker.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker.java similarity index 92% rename from src/test/java/beforev14/dependnew/DeWorker.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker.java index 135b6b3..b5b0b01 100755 --- a/src/test/java/beforev14/dependnew/DeWorker.java +++ b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker implements IWorker, ICallback { @Override - public User action(String object, Map allWrappers) { + public User action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/dependnew/DeWorker1.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker1.java similarity index 94% rename from src/test/java/beforev14/dependnew/DeWorker1.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker1.java index ba02503..79418db 100755 --- a/src/test/java/beforev14/dependnew/DeWorker1.java +++ b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker1.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker1 implements IWorker, ICallback { @Override - public User action(String object, Map allWrappers) { + public User action(String object, Map> allWrappers) { System.out.println("-----------------"); System.out.println("获取par0的执行结果: " + allWrappers.get("first").getWorkResult()); System.out.println("取par0的结果作为自己的入参,并将par0的结果加上一些东西"); diff --git a/src/test/java/beforev14/dependnew/DeWorker2.java b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker2.java similarity index 94% rename from src/test/java/beforev14/dependnew/DeWorker2.java rename to asyncTool-core/src/test/java/beforev14/dependnew/DeWorker2.java index 304df06..bba8e86 100755 --- a/src/test/java/beforev14/dependnew/DeWorker2.java +++ b/asyncTool-core/src/test/java/beforev14/dependnew/DeWorker2.java @@ -14,7 +14,7 @@ import java.util.Map; class DeWorker2 implements IWorker, ICallback { @Override - public String action(User object, Map allWrappers) { + public String action(User object, Map> allWrappers) { System.out.println("-----------------"); System.out.println("par1的执行结果是: " + allWrappers.get("second").getWorkResult()); System.out.println("取par1的结果作为自己的入参,并将par1的结果加上一些东西"); diff --git a/src/test/java/beforev14/dependnew/Test.java b/asyncTool-core/src/test/java/beforev14/dependnew/Test.java similarity index 100% rename from src/test/java/beforev14/dependnew/Test.java rename to asyncTool-core/src/test/java/beforev14/dependnew/Test.java diff --git a/src/test/java/beforev14/dependnew/User.java b/asyncTool-core/src/test/java/beforev14/dependnew/User.java similarity index 100% rename from src/test/java/beforev14/dependnew/User.java rename to asyncTool-core/src/test/java/beforev14/dependnew/User.java diff --git a/src/test/java/beforev14/parallel/ParTimeoutWorker.java b/asyncTool-core/src/test/java/beforev14/parallel/ParTimeoutWorker.java similarity index 98% rename from src/test/java/beforev14/parallel/ParTimeoutWorker.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParTimeoutWorker.java index f0a2f3a..8e393ec 100755 --- a/src/test/java/beforev14/parallel/ParTimeoutWorker.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParTimeoutWorker.java @@ -15,7 +15,7 @@ import java.util.Map; class ParTimeoutWorker implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/ParWorker.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker.java similarity index 98% rename from src/test/java/beforev14/parallel/ParWorker.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker.java index b28b7e6..58f8694 100755 --- a/src/test/java/beforev14/parallel/ParWorker.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker.java @@ -15,7 +15,7 @@ import java.util.Map; class ParWorker implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/ParWorker1.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker1.java similarity index 98% rename from src/test/java/beforev14/parallel/ParWorker1.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker1.java index 414851c..87592bc 100755 --- a/src/test/java/beforev14/parallel/ParWorker1.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker1.java @@ -20,7 +20,7 @@ class ParWorker1 implements IWorker, ICallback { } @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/ParWorker2.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker2.java similarity index 98% rename from src/test/java/beforev14/parallel/ParWorker2.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker2.java index 87cc0ac..a456c33 100755 --- a/src/test/java/beforev14/parallel/ParWorker2.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker2.java @@ -20,7 +20,7 @@ class ParWorker2 implements IWorker, ICallback { } @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/ParWorker3.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker3.java similarity index 98% rename from src/test/java/beforev14/parallel/ParWorker3.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker3.java index 82b6299..148c6ab 100755 --- a/src/test/java/beforev14/parallel/ParWorker3.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker3.java @@ -19,7 +19,7 @@ class ParWorker3 implements IWorker, ICallback { this.sleepTime = sleepTime; } @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/ParWorker4.java b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker4.java similarity index 98% rename from src/test/java/beforev14/parallel/ParWorker4.java rename to asyncTool-core/src/test/java/beforev14/parallel/ParWorker4.java index 7f9c267..9046ee2 100755 --- a/src/test/java/beforev14/parallel/ParWorker4.java +++ b/asyncTool-core/src/test/java/beforev14/parallel/ParWorker4.java @@ -15,7 +15,7 @@ import java.util.Map; class ParWorker4 implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/parallel/TestPar.java b/asyncTool-core/src/test/java/beforev14/parallel/TestPar.java similarity index 100% rename from src/test/java/beforev14/parallel/TestPar.java rename to asyncTool-core/src/test/java/beforev14/parallel/TestPar.java diff --git a/src/test/java/beforev14/seq/SeqTimeoutWorker.java b/asyncTool-core/src/test/java/beforev14/seq/SeqTimeoutWorker.java similarity index 98% rename from src/test/java/beforev14/seq/SeqTimeoutWorker.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqTimeoutWorker.java index 80a5c7b..3297bcb 100755 --- a/src/test/java/beforev14/seq/SeqTimeoutWorker.java +++ b/asyncTool-core/src/test/java/beforev14/seq/SeqTimeoutWorker.java @@ -15,7 +15,7 @@ import java.util.Map; class SeqTimeoutWorker implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/seq/SeqWorker.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker.java similarity index 98% rename from src/test/java/beforev14/seq/SeqWorker.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker.java index c2bc392..ebb451a 100755 --- a/src/test/java/beforev14/seq/SeqWorker.java +++ b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker.java @@ -15,7 +15,7 @@ import java.util.Map; class SeqWorker implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/seq/SeqWorker1.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker1.java similarity index 98% rename from src/test/java/beforev14/seq/SeqWorker1.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker1.java index b3ded50..f1544d1 100755 --- a/src/test/java/beforev14/seq/SeqWorker1.java +++ b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker1.java @@ -15,7 +15,7 @@ import java.util.Map; class SeqWorker1 implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/seq/SeqWorker2.java b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker2.java similarity index 98% rename from src/test/java/beforev14/seq/SeqWorker2.java rename to asyncTool-core/src/test/java/beforev14/seq/SeqWorker2.java index 458db80..1002f68 100755 --- a/src/test/java/beforev14/seq/SeqWorker2.java +++ b/asyncTool-core/src/test/java/beforev14/seq/SeqWorker2.java @@ -15,7 +15,7 @@ import java.util.Map; class SeqWorker2 implements IWorker, ICallback { @Override - public String action(String object, Map allWrappers) { + public String action(String object, Map> allWrappers) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/test/java/beforev14/seq/TestSequential.java b/asyncTool-core/src/test/java/beforev14/seq/TestSequential.java similarity index 100% rename from src/test/java/beforev14/seq/TestSequential.java rename to asyncTool-core/src/test/java/beforev14/seq/TestSequential.java diff --git a/src/test/java/beforev14/seq/TestSequentialTimeout.java b/asyncTool-core/src/test/java/beforev14/seq/TestSequentialTimeout.java similarity index 100% rename from src/test/java/beforev14/seq/TestSequentialTimeout.java rename to asyncTool-core/src/test/java/beforev14/seq/TestSequentialTimeout.java diff --git a/asyncTool-core/src/test/java/v15/cases/Case0.java b/asyncTool-core/src/test/java/v15/cases/Case0.java new file mode 100644 index 0000000..52fa6cf --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case0.java @@ -0,0 +1,37 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +import java.util.concurrent.ExecutionException; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:10 + */ +class Case0 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").build(); + WorkerWrapper c = builder("C").build(); + try { + Async.work(100, a, b, c).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case01.java b/asyncTool-core/src/test/java/v15/cases/Case01.java new file mode 100644 index 0000000..cf1194f --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case01.java @@ -0,0 +1,37 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:13 + */ +class Case01 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b = builder("B").depends(a).build(); + WorkerWrapper c = builder("C").depends(a).build(); + WorkerWrapper f = builder("F").depends(b, c).build(); + try { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=C) is working + wrapper(id=B) is working + wrapper(id=F) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case02.java b/asyncTool-core/src/test/java/v15/cases/Case02.java new file mode 100644 index 0000000..0c9a65d --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case02.java @@ -0,0 +1,38 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:15 + */ +class Case02 { + static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper f = builder("F").build(); + WorkerWrapper a = builder("A") + .nextOf(builder("B").nextOf(f).build()) + .nextOf(builder("C").nextOf(f).build()) + .build(); + try { + Async.work(100, a).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + wrapper(id=F) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case1.java b/asyncTool-core/src/test/java/v15/cases/Case1.java new file mode 100644 index 0000000..c0a1b74 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case1.java @@ -0,0 +1,62 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +import java.util.concurrent.ExecutionException; + +/** + * 示例:简单示例--复杂点的 + * + * @author create by TcSnZh on 2021/5/8-下午10:29 + */ +class Case1 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) { + WorkerWrapper a = builder("A").build(); + WorkerWrapper d; + builder("H") + .depends( + builder("F") + .depends(builder("B").depends(a).build()) + .depends(builder("C").depends(a).build()) + .build(), + builder("G") + .depends(builder("E") + .depends(d = builder("D").build()) + .build()) + .build() + ) + .build(); + try { + Async.work(1000, a, d).awaitFinish(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=D) is working + wrapper(id=A) is working + wrapper(id=E) is working + wrapper(id=B) is working + wrapper(id=C) is working + wrapper(id=G) is working + wrapper(id=F) is working + wrapper(id=H) is working + */ + } +} + diff --git a/asyncTool-core/src/test/java/v15/cases/Case10.java b/asyncTool-core/src/test/java/v15/cases/Case10.java new file mode 100644 index 0000000..2f6fcc6 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case10.java @@ -0,0 +1,72 @@ +package v15.cases; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.OnceWork; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; + +import java.util.concurrent.ExecutionException; + +/** + * @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/26-下午4:07 + */ +class Case10 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })) + .allowInterrupt(true); + } + + /** + * A(10ms) ==> B(10ms) ==> C(10ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + final WorkerWrapper c; + final WorkerWrapper b; + final WorkerWrapper a = builder("A", 10) + .nextOf(b = builder("B", 10) + .nextOf(c = builder("C", 10).build()) + .build()) + .build(); + final OnceWork onceWork = Async.work(40, a); + Thread.sleep(25); + onceWork.pleaseCancelAndAwaitFinish(); + System.out.println("任务b信息 " + b); + System.out.println("任务c信息 " + c); + System.out.println("OnceWork信息 " + onceWork); + /* + 可以看到C的state为SKIP,workResult.ex为CancelSkippedException,即被取消了。 + 不过有时程序运行慢,导致B被取消了,那么C就不会执行,其状态就为INIT了。 + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case2.java b/asyncTool-core/src/test/java/v15/cases/Case2.java new file mode 100644 index 0000000..1c930c7 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case2.java @@ -0,0 +1,53 @@ +package v15.cases; + +import com.jd.platform.async.callback.IWorker; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; + +import java.util.Map; +import java.util.concurrent.ExecutionException; + +/** + * 示例:简单示例--依赖别的worker执行结果作为入参 + * + * @author create by TcSnZh on 2021/5/8-下午10:46 + */ +class Case2 { + static class AddWork implements IWorker { + private final String id1; + private final String id2; + + public AddWork(String id1, String id2) { + this.id1 = id1; + this.id2 = id2; + } + + public AddWork() { + this(null, null); + } + + @Override + public Integer action(Integer param, Map> allWrappers) { + // 传入的参数 + if (param != null) { + return param; + } + // 将两个id所对应的wrapper的结果取出,相加并返回 + Integer i1 = (Integer) allWrappers.get(id1).getWorkResult().getResult(); + Integer i2 = (Integer) allWrappers.get(id2).getWorkResult().getResult(); + return i1 + i2; + } + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper wrapper100 = WorkerWrapper.builder() + .id("id:100").worker(new AddWork()).param(100).build(); + WorkerWrapper wrapper200 = WorkerWrapper.builder() + .id("id:200").worker(new AddWork()).param(200).build(); + WorkerWrapper add = WorkerWrapper.builder().id("id:add") + .worker(new AddWork("id:100", "id:200")).depends(wrapper100, wrapper200).build(); + Async.work(20,wrapper100,wrapper200).awaitFinish(); + System.out.println(add.getWorkResult()); + // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case3.java b/asyncTool-core/src/test/java/v15/cases/Case3.java new file mode 100644 index 0000000..8c5dc98 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case3.java @@ -0,0 +1,63 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 示例:设置依赖策略--快速上手 + * + * @author create by TcSnZh on 2021/5/8-下午10:58 + */ +class Case3 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A").build(); + WorkerWrapper b1 = builder("B1").depends(a).build(); + WorkerWrapper b2 = builder("B2").depends(a).build(); + WorkerWrapper b3 = builder("B3").depends(a).build(); + WorkerWrapper b4 = builder("B4").depends(a).build(); + WorkerWrapper b5 = builder("B5").depends(a).build(); + WorkerWrapper c1 = builder("C1") + .depends(DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS, b1, b2) + .build(); + WorkerWrapper c2 = builder("C2") + .depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS, b3, b4, b5) + .build(); + // 这里用线程数较少的线程池做示例,对于ALL_DEPENDENCIES_ANY_SUCCESS“仅需一个”的效果会好一点 + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, a).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B3) is working + wrapper(id=B1) is working + wrapper(id=B2) is working + wrapper(id=C2) is working + wrapper(id=C1) is working + wrapper(id=B4) is working + // 我们看到B5被跳过了,没有执行callback + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case4.java b/asyncTool-core/src/test/java/v15/cases/Case4.java new file mode 100644 index 0000000..e78a599 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case4.java @@ -0,0 +1,69 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.ResultState; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; + +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 示例:自定义全局策略 + * + * @author create by TcSnZh on 2021/5/8-下午11:28 + */ +class Case4 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A").build(); + WorkerWrapper c = builder("C") + .setDepend().strategy(new DependenceStrategy() { + @Override + public DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { + return dependWrappers.stream() + .filter(workerWrapper -> workerWrapper.getWorkResult().getResultState() == ResultState.SUCCESS) + .count() > 3 ? + DependenceAction.START_WORK.emptyProperty() + : DependenceAction.TAKE_REST.emptyProperty(); + } + }).end() + .build(); + for (int i = 1; i < 10; i++) { + builder("B" + i).depends(a).nextOf(c).build(); + } + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, a).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B2) is working + wrapper(id=B1) is working + wrapper(id=B4) is working + wrapper(id=B3) is working + wrapper(id=B5) is working + wrapper(id=C) is working + 由于B1-B10是并行的,所以正好仅有3个wrapper成功,在多线程环境中是比较难遇到的。 + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case5.java b/asyncTool-core/src/test/java/v15/cases/Case5.java new file mode 100644 index 0000000..5b3c99b --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case5.java @@ -0,0 +1,75 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; + +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 示例:设置一组必须完成的wrapper(不推荐使用) + * + * @author create by TcSnZh on 2021/5/9-上午1:06 + */ +class Case5 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + @Deprecated + public static void main(String[] args) throws ExecutionException, InterruptedException { + + WorkerWrapper a1 = builder("A1").build(); + WorkerWrapper a2 = builder("A2").build(); + WorkerWrapper a3 = builder("A3").build(); + WorkerWrapper a4 = builder("A4").build(); + WorkerWrapper a5 = builder("A5").build(); + WorkerWrapper a6 = builder("A6").build(); + WorkerWrapper a7 = builder("A7").build(); + WorkerWrapper a8 = builder("A8").build(); + WorkerWrapper a9 = builder("A9").build(); + WorkerWrapper a10 = builder("A10").build(); + builder("B") + .setDepend() + // 必须a3、a4成功才能执行 + .mustRequireWrapper(a3, a4) + // 如果a3、a4没有成功,则休息 + .strategy((dependWrappers, thisWrapper, fromWrapper) -> DependenceAction.TAKE_REST.emptyProperty()) + .wrapper(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + .end() + .build(); + WorkerWrapper start = builder("start").nextOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).build(); + ExecutorService pool = Executors.newFixedThreadPool(2); + try { + Async.work(1000, pool, start).awaitFinish(); + } finally { + pool.shutdown(); + } + /* 输出: + wrapper(id=A1) is working + wrapper(id=A2) is working + wrapper(id=A4) is working + wrapper(id=A3) is working + wrapper(id=A5) is working + wrapper(id=B) is working + wrapper(id=A6) is working + 我们可以看到,A3、A4执行后,B也执行了,之后的wrapper被跳过了 + (这里之所以a5、a6还在执行,只是因为他两正好在WORKING,所以没发现后面的B已经可以跳过了) + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case6.java b/asyncTool-core/src/test/java/v15/cases/Case6.java new file mode 100644 index 0000000..9de0258 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case6.java @@ -0,0 +1,59 @@ +package v15.cases; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.ResultState; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; + +import java.util.concurrent.ExecutionException; + +/** + * 示例:自定义依赖策略--对单个wrapper设置“上克下”策略--简单使用与示例 + * + * @author create by TcSnZh on 2021/5/9-上午1:42 + */ +class Case6 { + private static WorkerWrapperBuilder builder(String id) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + }); + } + + public static void main(String[] args) throws ExecutionException, InterruptedException { + //noinspection unchecked + WorkerWrapper b = builder("B") + // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 + .depends((dependWrappers, thisWrapper, fromWrapper) -> + DependenceAction.FAST_FAIL + .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) + ) + .callback(ICallback.PRINT_EXCEPTION_STACK_TRACE) + .build(); + WorkerWrapper a = builder("A") + .setNext() + // a将会使b直接开始工作 + // 若是去掉这行代码,则b会失败 + .specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b) + .wrapper(b) + .end().build(); + Async.work(1000, a).awaitFinish(); + System.out.println(a.getWorkResult()); + System.out.println(b.getWorkResult()); + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + WorkResult{result=null, resultState=SUCCESS, ex=null} + WorkResult{result=null, resultState=SUCCESS, ex=null} + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case7.java b/asyncTool-core/src/test/java/v15/cases/Case7.java new file mode 100644 index 0000000..14c2e85 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case7.java @@ -0,0 +1,61 @@ +package v15.cases; + +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.ResultState; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; + +import java.util.concurrent.ExecutionException; + +/** + * @author create by TcSnZh on 2021/5/9-下午4:12 + */ +class Case7 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("wrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }); + } + + /** + * A ==> B(10ms) ==> C ==> D (D可在E、C任意一个完成后执行) + * . \====> E(5ms) ====/ + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper d = builder("D").depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS).build(); + WorkerWrapper a = builder("A") + .nextOf(builder("B", 10) + .nextOf(builder("C") + .nextOf(d) + // 这里我们没有设置C的跳过策略,因为默认使用CHECK_ONE_LEVEL,可将下行代码注释去掉,则C会执行 +// .setSkipStrategy(SkipStrategy.NOT_SKIP) + .build()) + .build(), + builder("E", 5).nextOf(d).build() + ).build(); + Async.work(1000, a).awaitFinish(); + /* 输出: + wrapper(id=A) is working + wrapper(id=E) is working + wrapper(id=B) is working + wrapper(id=D) is working + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case8.java b/asyncTool-core/src/test/java/v15/cases/Case8.java new file mode 100644 index 0000000..bbc5def --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case8.java @@ -0,0 +1,74 @@ +package v15.cases; + +import com.jd.platform.async.callback.ICallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.worker.WorkResult; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapperBuilder; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; + +import java.util.concurrent.ExecutionException; + +/** + * @author create by TcSnZh on 2021/5/9-下午4:34 + */ +class Case8 { + private static WorkerWrapperBuilder builder(String id) { + return builder(id, -1L); + } + + private static WorkerWrapperBuilder builder(String id, long sleepTime) { + return WorkerWrapper.builder() + .id(id) + .worker((param, allWrappers) -> { + System.out.println("\twrapper(id=" + id + ") is working"); + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + }) + .callback((new ICallback() { + @Override + public void begin() { + System.out.println("wrapper(id=" + id + ") has begin . "); + } + + @Override + public void result(boolean success, String param, WorkResult workResult) { + System.out.println("\t\twrapper(id=" + id + ") callback " + + (success ? "success " : "fail ") + + ", workResult is " + workResult); + } + })); + } + + /** + * A ==> B(10ms) ==> C(20ms) + */ + public static void main(String[] args) throws ExecutionException, InterruptedException { + WorkerWrapper a = builder("A") + .nextOf(builder("B", 10) + .nextOf(builder("C", 20).build()) + .build()) + .build(); + Async.work(20, a).awaitFinish(); + /* 输出: + wrapper(id=A) has begin . + wrapper(id=A) is working + wrapper(id=A) callback success , workResult is WorkResult{result=null, resultState=SUCCESS, ex=null} + wrapper(id=B) has begin . + wrapper(id=B) is working + wrapper(id=B) callback success , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} + wrapper(id=C) has begin . + wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null} + java.lang.InterruptedException: sleep interrupted + at java.lang.Thread.sleep(Native Method) + ... + 以下异常信息省略 + */ + } +} diff --git a/asyncTool-core/src/test/java/v15/cases/Case9.java b/asyncTool-core/src/test/java/v15/cases/Case9.java new file mode 100644 index 0000000..d58c4d7 --- /dev/null +++ b/asyncTool-core/src/test/java/v15/cases/Case9.java @@ -0,0 +1,65 @@ +package v15.cases; + +import com.jd.platform.async.callback.DefaultCallback; +import com.jd.platform.async.executor.Async; +import com.jd.platform.async.openutil.collection.CommonDirectedGraph; +import com.jd.platform.async.openutil.collection.DirectedGraph; +import com.jd.platform.async.wrapper.QuickBuildWorkerWrapper; +import com.jd.platform.async.wrapper.WorkerWrapper; +import com.jd.platform.async.wrapper.strategy.WrapperStrategy; + +import java.util.concurrent.*; + +/** + * 快速构造示例。 + * + * @author create by TcSnZh on 2021/5/17-下午5:23 + */ +class Case9 { + public static void main(String[] args) throws ExecutionException, InterruptedException { + DirectedGraph, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>()); + QuickBuildWorkerWrapper w1 = new QuickBuildWorkerWrapper<>("id1", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 1"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + QuickBuildWorkerWrapper w2 = new QuickBuildWorkerWrapper<>("id2", + null, + (object, allWrappers) -> { + System.out.println("I am IWorker 2"); + return null; + }, + new DefaultCallback<>(), + false, + true, + 100, + TimeUnit.MILLISECONDS, + new WrapperStrategy.DefaultWrapperStrategy(), + graph + ); + graph.addNode(w1, w2); + graph.putRelation(w1, new Object(), w2); + +// System.out.println(graph); + + Async.work(200, w1).awaitFinish(); + + System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n"); + /* 输出: + I am IWorker 1 + I am IWorker 2 + Begin work end . + w1 : 省略 + w2 : 省略 + */ + } +} diff --git a/src/test/java/v15/dependnew/Test.java b/asyncTool-core/src/test/java/v15/wrappertest/Test.java similarity index 96% rename from src/test/java/v15/dependnew/Test.java rename to asyncTool-core/src/test/java/v15/wrappertest/Test.java index d0b149b..696e3f6 100644 --- a/src/test/java/v15/dependnew/Test.java +++ b/asyncTool-core/src/test/java/v15/wrappertest/Test.java @@ -1,14 +1,14 @@ -package v15.dependnew; +package v15.wrappertest; import com.jd.platform.async.callback.IWorker; import com.jd.platform.async.executor.Async; import com.jd.platform.async.executor.timer.SystemClock; import com.jd.platform.async.worker.ResultState; -import com.jd.platform.async.wrapper.actionstrategy.DependenceAction; -import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; import com.jd.platform.async.wrapper.WorkerWrapper; import com.jd.platform.async.wrapper.WorkerWrapperBuilder; -import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy; +import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; import java.io.PrintStream; import java.util.*; @@ -136,7 +136,7 @@ class Test { } /** - * 测试旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况: + * 测试旧版本(v1.4)中可能会引发线程耗尽bug的情况: *

* A(5ms)--B1(10ms) ---|--> C1(5ms) * . \ | (B1、B2全部完成可执行C1、C2) @@ -195,10 +195,10 @@ class Test { // B4、B5总任务超时 .nextOf(testBuilder("B4", 250).build()) .nextOf(testBuilder("B5", 250) - .setTimeOut().enableTimeOut(true).setTime(300, TimeUnit.MILLISECONDS).allowInterrupt(false).end() + .setTimeOut().enableTimeOut(true).setTime(300, TimeUnit.MILLISECONDS).end() .build()) // 测试打断B6线程 - .nextOf(testBuilder("B6", 250).timeout(true, 150, TimeUnit.MILLISECONDS, true).build()) + .nextOf(testBuilder("B6", 250).timeout(true, 150, TimeUnit.MILLISECONDS).allowInterrupt(true).build()) .build(); long t1 = SystemClock.now(); boolean success = Async.beginWork(200, pool, a); diff --git a/asyncTool-openutil/pom.xml b/asyncTool-openutil/pom.xml new file mode 100644 index 0000000..e0603b8 --- /dev/null +++ b/asyncTool-openutil/pom.xml @@ -0,0 +1,19 @@ + + + + asyncTool + com.jd.platform + 1.5.1-SNAPSHOT + + 4.0.0 + + asyncTool-openutil + + + 8 + 8 + + + \ No newline at end of file diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java new file mode 100644 index 0000000..5902bd6 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/BiInt.java @@ -0,0 +1,109 @@ +package com.jd.platform.async.openutil; + +import java.util.Comparator; + +/** + * 两个int值包装类。重写了{@link #hashCode()}与{@link #equals(Object)} + * + * @author create by TcSnZh on 2021/5/16-上午1:50 + */ +public final class BiInt { + // properties + + private final int m; + private final int n; + + public static final Comparator cmp_m_asc = Comparator.comparingInt(BiInt::getM); + public static final Comparator cmp_n_asc = Comparator.comparingInt(BiInt::getN); + public static final Comparator cmp_m_desc = cmp_m_asc.reversed(); + public static final Comparator cmp_n_desc = cmp_n_asc.reversed(); + public static final Comparator cmp_m_asc_n_asc = + cmp_m_asc.thenComparing(cmp_n_asc); + public static final Comparator cmp_m_asc_n_desc = + cmp_m_asc.thenComparing(cmp_n_desc); + public static final Comparator cmp_m_desc_n_asc = + cmp_m_desc.thenComparing(cmp_n_asc); + public static final Comparator cmp_m_desc_n_desc = + cmp_m_desc.thenComparing(cmp_n_desc); + public static final Comparator cmp_n_asc_m_asc = + cmp_n_asc.thenComparing(cmp_m_asc); + public static final Comparator cmp_n_asc_m_desc = + cmp_n_asc.thenComparing(cmp_m_desc); + public static final Comparator cmp_n_desc_m_asc = + cmp_n_desc.thenComparing(cmp_m_asc); + public static final Comparator cmp_n_desc_m_desc = + cmp_n_desc.thenComparing(cmp_m_desc); + + /** + * private constructor , please use {@link #of(int, int)} to build Idx object. + */ + private BiInt(int m, int n) { + this.m = m; + this.n = n; + } + + // getter + + public int getM() { + return m; + } + + public int getN() { + return n; + } + + // hashcode and equals + + @Override + public int hashCode() { + return m ^ n; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BiInt)) + return false; + BiInt idx = (BiInt) o; + return m == idx.m && n == idx.n; + } + + // toString + + @Override + public String toString() { + return "(" + m + ',' + n + ')'; + } + + // ========== static ========== + + + // 工厂方法 + + public static BiInt of(int m, int n) { + if (m == Integer.MIN_VALUE && n == Integer.MAX_VALUE) { + return MIN_TO_MAX; + } + if (m >= 0 && m < CACHE_RANGE_M && n >= 0 && n < CACHE_RANGE_M) { + return cache[m * CACHE_RANGE_M + n]; + } + return new BiInt(m, n); + } + + // 缓存区间 + + private static final BiInt MIN_TO_MAX = new BiInt(Integer.MIN_VALUE, Integer.MAX_VALUE); + private static final BiInt[] cache; // m from 0 to 31 , n from 0 to 31 , total 1023 . + private static final int CACHE_RANGE_M = 32; // 0 to 31 + private static final int CACHE_RANGE_N = 32; // 0 to 31 + + static { + cache = new BiInt[CACHE_RANGE_M * CACHE_RANGE_N]; + for (int i = 0; i < CACHE_RANGE_M; i++) { + for (int j = 0; j < CACHE_RANGE_N; j++) { + cache[i * CACHE_RANGE_M + j] = new BiInt(i, j); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java new file mode 100644 index 0000000..66c8939 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractArray2D.java @@ -0,0 +1,74 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.Iterator; + +/** + * @author create by TcSnZh on 2021/5/14-下午9:51 + */ +public abstract class AbstractArray2D implements Array2D { + /** + * 用于代替null + */ + protected static final Object NULL = new Object() { + @Override + public String toString() { + return "null"; + } + + @Override + public int hashCode() { + return 0; + } + + @SuppressWarnings("EqualsDoesntCheckParameterClass") + @Override + public boolean equals(Object obj) { + //noinspection ConstantConditions + return obj == null || obj == NULL || obj.equals(null); + } + }; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64).append(this.getClass().getSimpleName()).append('{'); + Iterator> it = iterator(); + if (it.hasNext()) { + while (true) { + Point point = it.next(); + sb.append('{').append(point.getIdx()).append(':').append(point.getElement()).append('}'); + if (!it.hasNext()) { + break; + } + sb.append(", "); + } + } + return sb.append('}').toString(); + } + + public static class PointImpl implements Point { + private final BiInt idx; + private final E element; + + public PointImpl(BiInt idx, E element) { + this.idx = idx; + this.element = element; + } + + @Override + public BiInt getIdx() { + return idx; + } + + @Override + public E getElement() { + return element; + } + + @Override + public String toString() { + return "{" + idx + ":" + element + "}"; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java new file mode 100644 index 0000000..cb971d5 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractDirectedGraph.java @@ -0,0 +1,88 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; + +/** + * 抽象有向图 + * + * @author create by TcSnZh on 2021/5/13-上午11:37 + */ +public abstract class AbstractDirectedGraph implements DirectedGraph { + + @Override + public String toString() { + Set nv = nodesView(); + Set> rSet = getRelations(); + StringBuilder sb = new StringBuilder(nv.size() * 10 + rSet.size() * 20) + .append(this.getClass().getSimpleName()).append("{nodes=["); + Iterator nit = nodesView().iterator(); + if (nit.hasNext()) { + for (; ; ) { + sb.append(nit.next()); + if (!nit.hasNext()) { + break; + } + sb.append(", "); + } + } + sb.append("], relations=["); + Iterator> eit = rSet.iterator(); + if (eit.hasNext()) { + for (; ; ) { + sb.append(eit.next()); + if (!eit.hasNext()) { + break; + } + sb.append(", "); + } + } + return sb.append("]}").toString(); + } + + public abstract class AbstractNodesView extends AbstractSet { + @Override + public boolean add(N n) { + return AbstractDirectedGraph.this.addNode(n); + } + + @Override + public boolean remove(Object o) { + N o1; + //noinspection unchecked + if (!AbstractDirectedGraph.this.containsNode(o1 = (N) o)) { + return false; + } + AbstractDirectedGraph.this.removeNode(o1); + return true; + } + } + + public static abstract class AbstractEntry implements Entry { + @Override + public int hashCode() { + return this.getFrom().hashCode() ^ this.getTo().hashCode() ^ this.getRelation().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Graph.Entry)) { + return false; + } + Entry obj1 = (Entry) obj; + return Objects.equals(this.getFrom(), obj1.getFrom()) + && Objects.equals(this.getTo(), obj1.getTo()) + && Objects.equals(this.getRelation(), obj1.getRelation()); + } + + @Override + public String toString() { + return "{from=" + getFrom() + ", relation=" + getRelation() + ", to=" + getTo() + "]"; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java new file mode 100644 index 0000000..6caea3c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/AbstractStoreArk.java @@ -0,0 +1,22 @@ +package com.jd.platform.async.openutil.collection; + +/** + * @author create by TcSnZh on 2021/5/14-上午2:33 + */ +public abstract class AbstractStoreArk implements StoreArk { + + @Override + public boolean isEmpty() { + return size() <= 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(size() * 10).append(this.getClass().getSimpleName()).append("{"); + if (!isEmpty()) { + stream().forEach(entry -> sb.append(entry.getKey()).append(":").append(entry.getValue()).append(", ")); + sb.delete(sb.length() - 2, sb.length()); + } + return sb.append("}").toString(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java new file mode 100644 index 0000000..c4bcc64 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Array2D.java @@ -0,0 +1,169 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.*; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * 二维数组 + * + * @author create by TcSnZh on 2021/5/14-下午9:50 + */ +@SuppressWarnings("unused") +public interface Array2D extends Iterable> { + /** + * 有多少行 + */ + int lineLength(); + + /** + * 有多少列 + */ + int columnLength(); + + /** + * 添加元素到指定位置 + * + * @param line 行 + * @param column 列 + * @param element 元素 + * @return 如果之前添加过元素,将返回替换掉的之前的元素 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + E add(int line, int column, E element); + + /** + * 如果不存在的话则添加元素 + *

+ * {@link #add(int, int, Object)} + * + * @return 不存在且成功添加,返回true。 + */ + default boolean addIfAbsent(int line, int column, E element) { + if (get(line, column) != null) { + return false; + } + add(line, column, element); + return true; + } + + /** + * 删除元素 + * + * @param line 行 + * @param column 列 + * @return 返回移出的元素 + * @throws IndexOutOfBoundsException 行列超出返回 + * @throws IllegalArgumentException 如果原本不存在元素 + */ + E remove(int line, int column); + + /** + * 存在则移除,不存在则返回null + * + * @param line 行 + * @param column 列 + * @return 如果不存在,返回null。存在则返回被移出的元素。 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + default E removeIfAbsent(int line, int column) { + if (get(line, column) == null) { + return null; + } + return remove(line, column); + } + + /** + * 获取元素 + * + * @param line 行 + * @param column 列 + * @return 如果存在,返回该元素。不存在则返回null。 + * @throws IndexOutOfBoundsException 行列超出范围 + */ + E get(int line, int column); + + /** + * 是否包含元素 + * + * @param element 元素 + * @return 有这个元素就返回true。 + */ + boolean containsElement(E element); + + /** + * 获取整行的元素 + * + * @param line 行号 + * @return 返回key为列号,value为元素的Map + * @throws IndexOutOfBoundsException 行号超出范围 + */ + Map fullLine(int line); + + /** + * 获取整列的元素 + * + * @param column 列号 + * @return 返回key为行号,value为元素的Map + * @throws IndexOutOfBoundsException 列号超出范围 + */ + Map fullColumn(int column); + + /** + * 迭代器 + * + * @param foreachOrder 遍历顺序 + * @return 如果本容器不允许null值存在,只需返回存在的元素的键即可。如果允许null值存在,仅需返回包括人工放入的null值的键即可。 + */ + Iterator> iterator(Comparator foreachOrder); + + @Override + default Iterator> iterator() { + //noinspection unchecked + return (Iterator) iterator(BiInt.cmp_m_asc_n_asc); + } + + /** + * 流 + */ + default Stream> stream() { + return StreamSupport.stream(spliterator(), false); + } + + default Stream> parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + + default Spliterator> spliterator(Comparator foreachOrder) { + return Spliterators.spliteratorUnknownSize(iterator(foreachOrder), 0); + } + + default Stream> stream(Comparator foreachOrder) { + return StreamSupport.stream(spliterator(foreachOrder), false); + } + + default Stream> parallelStream(Comparator foreachOrder) { + return StreamSupport.stream(spliterator(foreachOrder), true); + } + + /** + * 端点 + * + * @param 元素泛型 + */ + interface Point { + BiInt getIdx(); + + default int getLine() { + return getIdx().getM(); + } + + default int getColumn() { + return getIdx().getN(); + } + + E getElement(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java new file mode 100644 index 0000000..a8a1362 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CachedStoreArk.java @@ -0,0 +1,60 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 一个缓存元素位置的储存柜。 + * + * @author create by TcSnZh on 2021/5/14-上午2:37 + */ +public class CachedStoreArk extends AbstractStoreArk { + private final StoreArk inner; + + private final Map cacheMap = new HashMap<>(); + + public CachedStoreArk() { + this(CommonStoreArk::new); + } + + private CachedStoreArk(Supplier> sup) { + this.inner = sup.get(); + } + + @Override + public int store(E element) { + int id = inner.store(element); + cacheMap.put(element, id); + return id; + } + + @Override + public E peek(int id) { + return inner.peek(id); + } + + @Override + public E takeOut(int id) { + E e = inner.takeOut(id); + cacheMap.remove(e); + return e; + } + + @Override + public int size() { + return inner.size(); + } + + @Override + public Iterator> iterator() { + return inner.iterator(); + } + + @Override + public int findId(E element) { + Integer idNullable = cacheMap.get(element); + return idNullable == null ? -1 : idNullable; + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java new file mode 100644 index 0000000..0683d2c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonDirectedGraph.java @@ -0,0 +1,145 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 线程不安全的有向图。 + *

+ * 不允许放入null。 + * + * @author create by TcSnZh on 2021/5/14-上午2:22 + */ +public class CommonDirectedGraph extends AbstractDirectedGraph { + + // ========== properties ========== + + private final StoreArk nodes = new CachedStoreArk<>(); + private final Array2D arr = new SparseArray2D<>(); + + // ========== methods ========== + + @Override + public boolean addNode(N node) { + if (containsNode(Objects.requireNonNull(node))) { + return false; + } + nodes.store(node); + return true; + } + + @Override + public boolean containsNode(N node) { + return node != null && findNodeId(node, false) >= 0; + } + + @Override + public Set> removeNode(N node) { + int id = findNodeId(Objects.requireNonNull(node), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为from的键 + arr.fullLine(id).forEach((toNodeId, relation) -> { + res.add(new OuterEntry<>(node, nodes.peek(toNodeId), relation)); + arr.remove(id, toNodeId); + }); + // 查找node为to的键 + arr.fullColumn(id).forEach((fromNodeId, relation) -> { + // 在上一次遍历中,fromNodeId为id, + if (fromNodeId == id) { + return; + } + res.add(new OuterEntry<>(nodes.peek(fromNodeId), node, relation)); + arr.remove(fromNodeId, id); + }); + nodes.takeOut(id); + return res; + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + return arr.add( + findNodeId(Objects.requireNonNull(fromNode), true), + findNodeId(Objects.requireNonNull(toNode), true), + Objects.requireNonNull(relation) + ); + } + + @Override + public Set> getRelationFrom(N from) { + int id = findNodeId(Objects.requireNonNull(from), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为from的键 + arr.fullLine(id).forEach((toNodeId, relation) -> res.add(new OuterEntry<>(from, nodes.peek(toNodeId), relation))); + return res; + } + + @Override + public Set> getRelationTo(N to) { + int id = findNodeId(Objects.requireNonNull(to), true); + LinkedHashSet> res = new LinkedHashSet<>(); + // 查找node为to的键 + arr.fullColumn(id).forEach((fromNodeId, relation) -> + res.add(new OuterEntry<>(nodes.peek(fromNodeId), to, relation))); + return res; + } + + @Override + public Set nodesView() { + return new AbstractNodesView() { + @Override + public Iterator iterator() { + return nodes.stream().map(Map.Entry::getValue).iterator(); + } + + @Override + public int size() { + return nodes.size(); + } + }; + } + + @Override + public Set> getRelations() { + return arr.stream().map((Function, Entry>) rPoint -> new OuterEntry<>( + nodes.peek(rPoint.getLine()), + nodes.peek(rPoint.getColumn()), + rPoint.getElement() + )).collect(Collectors.toSet()); + } + + private int findNodeId(N node, boolean mustExistElseThrowEx) { + int id = nodes.findId(Objects.requireNonNull(node)); + if (mustExistElseThrowEx && id < 0) { + throw new IllegalArgumentException("No node exists : " + node); + } + return id; + } + + private static class OuterEntry extends AbstractEntry { + private final N from; + private final N to; + private final R relation; + + public OuterEntry(N from, N to, R relation) { + this.from = from; + this.to = to; + this.relation = relation; + } + + @Override + public N getFrom() { + return from; + } + + @Override + public N getTo() { + return to; + } + + @Override + public R getRelation() { + return relation; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java new file mode 100644 index 0000000..ae05f6f --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/CommonStoreArk.java @@ -0,0 +1,159 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.*; + +/** + * 自动扩容的id储物柜,线程不安全。 + * + * @author create by TcSnZh on 2021/5/13-下午1:24 + */ +public class CommonStoreArk extends AbstractStoreArk { + private Object[] elements; + + /** + * 已经分配的下标数 + */ + private int allocSize = 0; + + /** + * 保存着最小空元素的队列 + */ + private final Queue emptyPoints = new PriorityQueue<>(Integer::compareTo); + + public CommonStoreArk(int initialCapacity) { + elements = new Object[initialCapacity]; + } + + public CommonStoreArk() { + this(10); + } + + @Override + public int store(E element) { + int id; + elements[id = pollId()] = element; + return id; + } + + @Override + public E peek(int id) { + if (id < 0) { + throw new IllegalArgumentException("id " + id + " can't be negative"); + } + if (id >= elements.length) { + return null; + } + //noinspection unchecked + return (E) elements[id]; + } + + @Override + public E takeOut(int id) { + if (id < 0) { + throw new IllegalArgumentException("id " + id + " can't be negative"); + } + if (id >= elements.length) { + return null; + } + //noinspection unchecked + E out = (E) elements[id]; + elements[id] = null; + if (id == allocSize - 1) { + allocSize--; + } else { + emptyPoints.add(id); + } + return out; + } + + @Override + public int size() { + return allocSize - emptyPoints.size(); + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + private final Map.Entry[] items; + + private int idx = 0; + + { + //noinspection unchecked + items = new Map.Entry[size()]; + int itemsIdx = 0; + Iterator emptyPointItr = emptyPoints.iterator(); + for (int i = 0; i < allocSize; i++) { + Object element = elements[i]; + if (element == null) { + continue; + } + final int _i = i; + //noinspection unchecked + items[itemsIdx++] = new Map.Entry() { + private final int k = _i; + private E v = (E) element; + + @Override + public Integer getKey() { + return k; + } + + @Override + public E getValue() { + return v; + } + + @Override + public E setValue(E value) { + E _v = this.v; + this.v = value; + return _v; + } + + @Override + public String toString() { + return "{" + k + ':' + v + '}'; + } + }; + } + } + + + @Override + public boolean hasNext() { + return idx < items.length; + } + + @Override + public Map.Entry next() { + return items[idx++]; + } + }; + } + + @Override + public int findId(E element) { + int i = 0; + for (Object o : elements) { + if (Objects.equals(o, element)) { + return i; + } + i++; + } + return -1; + } + + private int pollId() { + if (!emptyPoints.isEmpty()) { + return emptyPoints.poll(); + } + int id = allocSize++; + int length = elements.length; + if (id >= length) { + // 扩容 + elements = Arrays.copyOf(elements, Math.max(length + 1, length + (length >> 1))); + } + return id; + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java new file mode 100644 index 0000000..5ac6dc7 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/DirectedGraph.java @@ -0,0 +1,182 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.*; + +/** + * @author create by TcSnZh on 2021/5/16-下午11:27 + */ +public interface DirectedGraph extends Graph { + @Override + default boolean isDirected() { + return true; + } + + static DirectedGraph readOnlyDigraph(DirectedGraph source) { + return new ReadOnlyDirectedGraph<>(source); + } + + static DirectedGraph synchronizedDigraph(DirectedGraph source) { + return synchronizedDigraph(source, new Object()); + } + + static DirectedGraph synchronizedDigraph(DirectedGraph source, Object mutex) { + return new SyncDirectedGraph<>(source, mutex); + } + + class ReadOnlyDirectedGraph extends AbstractDirectedGraph { + private final DirectedGraph source; + + public ReadOnlyDirectedGraph(DirectedGraph source) { + this.source = source; + } + + private static UnsupportedOperationException readOnlyGraph() { + return new UnsupportedOperationException("readOnly graph"); + } + + @Override + public boolean addNode(N node) { + throw readOnlyGraph(); + } + + @Override + public boolean containsNode(N node) { + return source.containsNode(node); + } + + @Override + public Set> removeNode(N node) { + throw readOnlyGraph(); + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + throw readOnlyGraph(); + } + + @Override + public Set> getRelationFrom(N from) { + return source.getRelationFrom(from); + } + + @Override + public Set> getRelationTo(N to) { + return source.getRelationTo(to); + } + + @Override + public Set nodesView() { + return new AbstractSet() { + private final Set nodesViewSource = source.nodesView(); + + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator iteratorSource = nodesViewSource.iterator(); + + @Override + public boolean hasNext() { + return iteratorSource.hasNext(); + } + + @Override + public N next() { + return iteratorSource.next(); + } + + @Override + public void remove() { + throw readOnlyGraph(); + } + }; + } + + @Override + public int size() { + return nodesViewSource.size(); + } + + @Override + public boolean add(N n) { + throw readOnlyGraph(); + } + + @Override + public boolean remove(Object o) { + throw readOnlyGraph(); + } + }; + } + + @Override + public Set> getRelations() { + return source.getRelations(); + } + } + + class SyncDirectedGraph extends AbstractDirectedGraph { + private final DirectedGraph source; + private final Object mutex; + + public SyncDirectedGraph(DirectedGraph source, Object mutex) { + this.source = source; + this.mutex = mutex; + } + + @Override + public boolean addNode(N node) { + synchronized (mutex) { + return source.addNode(node); + } + } + + @Override + public boolean containsNode(N node) { + synchronized (mutex) { + return source.containsNode(node); + } + } + + @Override + public Set> removeNode(N node) { + synchronized (mutex) { + return source.removeNode(node); + } + } + + @Override + public R putRelation(N fromNode, R relation, N toNode) { + synchronized (mutex) { + return source.putRelation(fromNode, relation, toNode); + } + } + + @Override + public Set> getRelationFrom(N from) { + synchronized (mutex) { + return source.getRelationFrom(from); + } + } + + @Override + public Set> getRelationTo(N to) { + synchronized (mutex) { + return source.getRelationTo(to); + } + } + + @Override + public Set nodesView() { + synchronized (mutex) { + return Collections.synchronizedSet(source.nodesView()); + } + } + + @Override + public Set> getRelations() { + synchronized (mutex) { + return source.getRelations(); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java new file mode 100644 index 0000000..e9903ff --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/Graph.java @@ -0,0 +1,106 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Set; + +/** + * 图数据结构 + * + * @author create by TcSnZh on 2021/5/13-上午11:37 + */ +@SuppressWarnings("unused") +public interface Graph { + /** + * 添加节点。 + * 如果节点已经存在,则不会添加。 + * + * @param node 添加进图的节点 + * @return 添加成功返回true,如果节点已经存在返回false。 + */ + boolean addNode(N node); + + /** + * 添加一堆Node,任一成功返回true + */ + default boolean addNode(N... nodes) { + boolean success = false; + for (N node : nodes) { + if (addNode(node)) { + success = true; + } + } + return success; + } + + /** + * 是否存在节点 + * + * @param node 节点。 + * @return 存在返回true,否则返回false。 + */ + boolean containsNode(N node); + + /** + * 移除节点。 + * 返回与该节点有关系的,被一并移出的键。 + * + * @param node 节点 + * @return 返回值不会为null。 + * @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出异常。 + */ + Set> removeNode(N node); + + /** + * 添加关系 + * 在无向图中fromNode与toNode参数的位置调换没有影响。 + * + * @param fromNode 从这个节点开始 + * @param relation 关系 + * @param toNode 以那个节点为目标 + * @return 如果之前存在关系,则会替换之前的关系,返回出被替换的之前存在的关系。如果之前没有关系,返回null。 + * @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出该异常。 + */ + R putRelation(N fromNode, R relation, N toNode); + + /** + * 获取“从这个节点开始”的所有关系 + * + * @param from 关系开始的节点 + * @return 返回 {@link Entry}键。 + */ + Set> getRelationFrom(N from); + + /** + * 获取“以这个节点为目标”的所有关系 + * + * @param to 被关系的节点 + * @return 返回 {@link Entry}键。 + */ + Set> getRelationTo(N to); + + /** + * 返回全部节点视图 + * + * @return 视图 + */ + Set nodesView(); + + /** + * 返回全部关系,返回的是新Set + * + * @return 与本类无关的Set + */ + Set> getRelations(); + + /** + * 是否有向 + */ + boolean isDirected(); + + interface Entry { + N getFrom(); + + N getTo(); + + R getRelation(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java new file mode 100644 index 0000000..39dee10 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/SparseArray2D.java @@ -0,0 +1,230 @@ +package com.jd.platform.async.openutil.collection; + +import com.jd.platform.async.openutil.BiInt; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 稀疏二维数组。 + *

+ * 可以设置是否允许存入null。 + * + * @author create by TcSnZh on 2021/5/14-下午9:45 + */ +public class SparseArray2D extends AbstractArray2D { + + // ========== properties ========== + + /** + * 限制长宽,默认为Integer.MAX_VALUE。稀疏数组不在乎这些。 + */ + private final int maxLineLength; + private final int maxColumnLength; + private final boolean allowNull; + + private final Map items = new HashMap<>(); + + // ========== index cache properties ========== + + /** + * 缓存行列索引 + */ + private final NavigableMap> indexOfLine2columns = new TreeMap<>(Integer::compareTo); + private final NavigableMap> indexOfColumn2lines = new TreeMap<>(Integer::compareTo); + + // ========== constructor ========== + + public SparseArray2D() { + this(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public SparseArray2D(boolean allowNull) { + this(Integer.MAX_VALUE, Integer.MAX_VALUE, allowNull); + } + + public SparseArray2D(int maxLineCapacity, int maxColumnCapacity) { + this(maxLineCapacity, maxColumnCapacity, false); + } + + public SparseArray2D(int maxLineCapacity, int maxColumnCapacity, boolean allowNull) { + this.maxLineLength = maxLineCapacity; + this.maxColumnLength = maxColumnCapacity; + this.allowNull = allowNull; + } + + // ========== public methods ========== + @Override + public int lineLength() { + return maxLineLength; + } + + @Override + public int columnLength() { + return maxColumnLength; + } + + @Override + public E add(int line, int column, E element) { + if (!allowNull && element == null) { + throw new NullPointerException("null is not allowed"); + } + Object put = items.put(BiInt.of(checkLine(line), checkColumn(column)), element == null ? NULL : element); + addIndex(line, column); + //noinspection unchecked + return NULL.equals(put) ? null : (E) put; + } + + @Override + public E remove(int line, int column) { + BiInt idx = BiInt.of(checkLine(line), checkColumn(column)); + Object get = items.get(idx); + if (get == null) { + throw new IllegalArgumentException("There is no element in line " + line + " column " + column); + } + items.remove(idx); + removeIndex(line, column); + //noinspection unchecked + return NULL.equals(get) ? null : (E) get; + } + + /** + * 该方法如果返回null,则分不清 之前存入了null 还是 没有存入过 + *

+ * {@inheritDoc} + */ + @Override + public E get(int line, int column) { + Object get = items.get(BiInt.of(checkLine(line), checkColumn(column))); + //noinspection unchecked + return NULL.equals(get) ? null : (E) get; + } + + @Override + public boolean containsElement(E element) { + if (NULL.equals(element)) { + if (!allowNull) { + return false; + } + return items.values().stream().anyMatch(v -> NULL.equals(element)); + } + return items.values().stream().anyMatch(element::equals); + } + + @Override + public Map fullLine(int line) { + return Optional.ofNullable(indexOfLine2columns.get(line)) + .map(set -> set.stream() + .collect(Collectors.toMap(column -> column, column -> { + //noinspection unchecked + return (E) items.get(BiInt.of(line, column)); + }))) + .orElse(Collections.emptyMap()); + } + + @Override + public Map fullColumn(int column) { + return Optional.ofNullable(indexOfColumn2lines.get(column)) + .map(set -> set.stream() + .collect(Collectors.toMap(line -> line, line -> { + //noinspection unchecked + return (E) items.get(BiInt.of(line, column)); + }))) + .orElse(Collections.emptyMap()); + } + + @Override + public Iterator> iterator(Comparator foreachOrder) { + return new Iterator>() { + private final Iterator> it; + private Point last = null; + private boolean removed = false; + + { + it = items.entrySet().stream() + .sorted((o1, o2) -> foreachOrder.compare(o1.getKey(), o2.getKey())) + .iterator(); + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Point next() { + Map.Entry next = it.next(); + removed = false; + Object v = next.getValue(); + //noinspection unchecked + return last = new PointImpl<>(next.getKey(), NULL.equals(v) ? null : (E) v); + } + + @Override + public void remove() { + if (last == null || removed) { + throw new IllegalStateException(last == null + ? "Iterator has not yet been called .next() ." + : "Iterator item already removed : " + last); + } + BiInt idx = last.getIdx(); + SparseArray2D.this.remove(idx.getM(), idx.getN()); + } + }; + } + + // ========== private methods ========== + + private int checkLine(int line) { + int len = lineLength(); + if (line < 0 || line >= len) { + throw new IndexOutOfBoundsException("Line " + line + " out of bound [0," + (len - 1) + "]"); + } + return line; + } + + private int checkColumn(int column) { + int len = columnLength(); + if (column < 0 || column >= len) { + throw new IndexOutOfBoundsException("Column " + column + " out of bound [0," + (len - 1) + "]"); + } + return column; + } + + private void addIndex(int line, int column) { + indexOfLine2columns.computeIfAbsent(line, line1 -> new TreeSet<>(Integer::compareTo)).add(column); + indexOfColumn2lines.computeIfAbsent(column, column1 -> new TreeSet<>(Integer::compareTo)).add(line); + + } + + private void removeIndex(int line, int column) { + // remove line index + { + NavigableSet columns = indexOfLine2columns.get(line); + if (columns == null || !columns.contains(column)) { + throw new ConcurrentModificationException( + "线程不安全导致索引异常 : lines " + columns + " is null or not contain line " + line); + + } + if (columns.size() == 1) { + indexOfLine2columns.remove(line); + } else { + columns.remove(column); + } + } + // remove column index + { + NavigableSet lines = indexOfColumn2lines.get(column); + if (lines == null || !lines.contains(line)) { + throw new ConcurrentModificationException( + "线程不安全导致索引异常 : lines " + lines + " is null or not contain column " + column); + } + if (lines.size() == 1) { + indexOfColumn2lines.remove(column); + } else { + lines.remove(column); + } + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java new file mode 100644 index 0000000..063413c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/collection/StoreArk.java @@ -0,0 +1,69 @@ +package com.jd.platform.async.openutil.collection; + +import java.util.Map; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * id存储柜。 + * 每个元素的id是固定的(除非取出后重新加入),且id是大于等于0,且分配到的id必须是未分配的id中最小的。 + *

+ * 类似于我们去游泳馆,里面的存放个人物品的柜子。 + * 放进去元素后,会分配一个id。然后凭借该id取出元素。 + * 不过不同于这些现实中的柜子的是,这个存储柜必定会提供最小的id,并且必定>0。 + *

+ * + * @author create by TcSnZh on 2021/5/14-上午2:29 + */ +public interface StoreArk extends Iterable> { + /** + * 存入元素 + * + * @param element 元素。 + * @return 返回最小的id。从0开始。 + */ + int store(E element); + + /** + * 查看元素 + * + * @param id id; + * @return 返回存在的元素。如果本id未被占用 或 原先存入null,返回null。 + * @throws IllegalArgumentException id为负数时抛出该异常 + */ + E peek(int id); + + /** + * 取出元素 + * + * @param id id + * @return 返回被取出的元素。如果本id未被占用 或 原先存入null,返回null。 + * @throws IllegalArgumentException id为负数时抛出该异常 + */ + E takeOut(int id); + + /** + * 元素个数 + */ + int size(); + + /** + * 是否为空 + */ + boolean isEmpty(); + + /** + * 查找元素的id + * + * @param element 元素 + * @return 如果存在,返回id。不存在返回-1 + */ + int findId(E element); + + /** + * 返回流 + */ + default Stream> stream() { + return StreamSupport.stream(spliterator(), false); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java new file mode 100644 index 0000000..d26fa1e --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/AbstractWheelTimer.java @@ -0,0 +1,18 @@ +package com.jd.platform.async.openutil.timer; + +/** + * @author create by TcSnZh on 2021/5/12-下午6:36 + */ +public abstract class AbstractWheelTimer implements Timer, AutoCloseable { + public static final int WORKER_STATE_INIT = 0; + public static final int WORKER_STATE_STARTED = 1; + public static final int WORKER_STATE_SHUTDOWN = 2; + + public abstract void start(); + + @SuppressWarnings("RedundantThrows") + @Override + public void close() throws Exception { + stop(); + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java new file mode 100644 index 0000000..90df6e9 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/HashedWheelTimer.java @@ -0,0 +1,665 @@ +package com.jd.platform.async.openutil.timer; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 从netty里抄来的,删去了一些功能。 + *

+ * + * 如果违反开源协议,请联系作者: zh.jobs@foxmail.com + * If violate the open source agreement, please contact the author : zh0u.he@qq.com + * + * + * @author create by TcSnZh on 2021/5/12-下午7:16 + */ +public class HashedWheelTimer extends AbstractWheelTimer { + + private static final long MILLISECOND_NANOS = TimeUnit.MILLISECONDS.toNanos(1); + + private final Worker worker = new Worker(); + private final Thread workerThread; + @SuppressWarnings({"unused", "FieldMayBeFinal"}) + private final AtomicInteger workerState = new AtomicInteger(WORKER_STATE_INIT); // 0 - init, 1 - started, 2 - shut down + + private final long tickDuration; + private final HashedWheelBucket[] wheel; + private final int mask; + private final CountDownLatch startTimeInitialized = new CountDownLatch(1); + private final Queue timeouts = new ConcurrentLinkedDeque<>(); + private final Queue cancelledTimeouts = new ConcurrentLinkedDeque<>(); + private final AtomicLong pendingTimeouts = new AtomicLong(0); + private final long maxPendingTimeouts; + + private volatile long startTime; + + /** + * Creates a new timer with the default thread factory + * ({@link Executors#defaultThreadFactory()}), default tick duration, and + * default number of ticks per wheel. + */ + @SuppressWarnings("unused") + public HashedWheelTimer() { + this(Executors.defaultThreadFactory()); + } + + /** + * Creates a new timer with the default thread factory + * ({@link Executors#defaultThreadFactory()}) and default number of ticks + * per wheel. + * + * @param tickDuration the duration between tick + * @param unit the time unit of the {@code tickDuration} + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code tickDuration} is <= 0 + */ + @SuppressWarnings("unused") + public HashedWheelTimer(long tickDuration, TimeUnit unit) { + this(Executors.defaultThreadFactory(), tickDuration, unit); + } + + /** + * Creates a new timer with the default thread factory + * ({@link Executors#defaultThreadFactory()}). + * + * @param tickDuration the duration between tick + * @param unit the time unit of the {@code tickDuration} + * @param ticksPerWheel the size of the wheel + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 + */ + @SuppressWarnings("unused") + public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) { + this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel); + } + + /** + * Creates a new timer with the default tick duration and default number of + * ticks per wheel. + * + * @param threadFactory a {@link ThreadFactory} that creates a + * background {@link Thread} which is dedicated to + * {@link TimerTask} execution. + * @throws NullPointerException if {@code threadFactory} is {@code null} + */ + public HashedWheelTimer(ThreadFactory threadFactory) { + this(threadFactory, 100, TimeUnit.MILLISECONDS); + } + + /** + * Creates a new timer with the default number of ticks per wheel. + * + * @param threadFactory a {@link ThreadFactory} that creates a + * background {@link Thread} which is dedicated to + * {@link TimerTask} execution. + * @param tickDuration the duration between tick + * @param unit the time unit of the {@code tickDuration} + * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code tickDuration} is <= 0 + */ + public HashedWheelTimer( + ThreadFactory threadFactory, long tickDuration, TimeUnit unit) { + this(threadFactory, tickDuration, unit, 512); + } + + /** + * Creates a new timer. + * + * @param threadFactory a {@link ThreadFactory} that creates a + * background {@link Thread} which is dedicated to + * {@link TimerTask} execution. + * @param tickDuration the duration between tick + * @param unit the time unit of the {@code tickDuration} + * @param ticksPerWheel the size of the wheel + * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} + * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 + */ + public HashedWheelTimer( + ThreadFactory threadFactory, + long tickDuration, TimeUnit unit, int ticksPerWheel) { + this(threadFactory, tickDuration, unit, ticksPerWheel, -1); + } + + /** + * Creates a new timer. + * + * @param threadFactory a {@link ThreadFactory} that creates a + * background {@link Thread} which is dedicated to + * {@link TimerTask} execution. + * @param tickDuration the duration between tick + * @param unit the time unit of the {@code tickDuration} + * @param ticksPerWheel the size of the wheel + * @param maxPendingTimeouts The maximum number of pending timeouts after which call to + * {@code newTimeout} will result in + * {@link RejectedExecutionException} + * being thrown. No maximum pending timeouts limit is assumed if + * this value is 0 or negative. + * @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null} + * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0 + */ + public HashedWheelTimer(ThreadFactory threadFactory, + long tickDuration, + TimeUnit unit, + int ticksPerWheel, + long maxPendingTimeouts) { + + Objects.requireNonNull(threadFactory, "threadFactory must not null !"); + Objects.requireNonNull(threadFactory, "unit must not null !"); + if (tickDuration <= 0) { + throw new IllegalArgumentException("tickDuration should > 0 !"); + } + if (ticksPerWheel <= 0) { + throw new IllegalArgumentException("ticksPerWheel should > 0 !"); + } + + wheel = createWheel(ticksPerWheel); + mask = wheel.length - 1; + + long duration = unit.toNanos(tickDuration); + + // 检查一个周期是否比Long.MAX_VALUE还长 + if (duration >= Long.MAX_VALUE / wheel.length) { + throw new IllegalArgumentException(String.format( + "tickDuration: %d (expected: 0 < tickDuration in nanos < %d", + tickDuration, Long.MAX_VALUE / wheel.length)); + } + + this.tickDuration = Math.max(duration, MILLISECOND_NANOS); + workerThread = threadFactory.newThread(worker); + this.maxPendingTimeouts = maxPendingTimeouts; + } + + private static HashedWheelBucket[] createWheel(int ticksPerWheel) { + if (ticksPerWheel <= 0) { + throw new IllegalArgumentException( + "ticksPerWheel must be greater than 0: " + ticksPerWheel); + } + if (ticksPerWheel > 1073741824) { + throw new IllegalArgumentException( + "ticksPerWheel may not be greater than 2^30: " + ticksPerWheel); + } + + ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel); + HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel]; + for (int i = 0; i < wheel.length; i++) { + wheel[i] = new HashedWheelBucket(); + } + return wheel; + } + + private static int normalizeTicksPerWheel(int ticksPerWheel) { + int normalizedTicksPerWheel = 1; + while (normalizedTicksPerWheel < ticksPerWheel) { + normalizedTicksPerWheel <<= 1; + } + return normalizedTicksPerWheel; + } + + /** + * 显式启动后台线程。即使您没有调用此方法,后台线程也将根据需要自动启动。 + * + * @throws IllegalStateException 如果此计时器已停止{@link #stop()} + */ + @Override + public void start() { + switch (workerState.get()) { + case WORKER_STATE_INIT: + if (workerState.compareAndSet(WORKER_STATE_INIT, WORKER_STATE_STARTED)) { + workerThread.start(); + } + break; + case WORKER_STATE_STARTED: + break; + case WORKER_STATE_SHUTDOWN: + throw new IllegalStateException("cannot be started once stopped"); + default: + throw new Error("Invalid WorkerState"); + } + + // Wait until the startTime is initialized by the worker. + while (startTime == 0) { + try { + startTimeInitialized.await(); + } catch (InterruptedException ignore) { + // Ignore - it will be ready very soon. + } + } + } + + @Override + public Set stop() { + if (Thread.currentThread() == workerThread) { + throw new IllegalStateException( + HashedWheelTimer.class.getSimpleName() + + ".stop() cannot be called from " + + TimerTask.class.getSimpleName()); + } + + if (!workerState.compareAndSet(WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) { + // state is init or shutdown . + return Collections.emptySet(); + } + + + boolean interrupted = false; + while (workerThread.isAlive()) { + workerThread.interrupt(); + try { + workerThread.join(100); + } catch (InterruptedException ignored) { + interrupted = true; + } + } + + if (interrupted) { + Thread.currentThread().interrupt(); + } + + return worker.unprocessedTimeouts(); + } + + @Override + public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) { + Objects.requireNonNull(task, "task require not null !"); + Objects.requireNonNull(unit, "unit require not null !"); + + long pendingTimeoutsCount = pendingTimeouts.incrementAndGet(); + + if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) { + pendingTimeouts.decrementAndGet(); + throw new RejectedExecutionException("Number of pending timeouts (" + + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending " + + "timeouts (" + maxPendingTimeouts + ")"); + } + + start(); + + // Add the timeout to the timeout queue which will be processed on the next tick. + // During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket. + long deadline = System.nanoTime() + unit.toNanos(delay) - startTime; + + // Guard against overflow. + if (delay > 0 && deadline < 0) { + deadline = Long.MAX_VALUE; + } + HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline); + timeouts.add(timeout); + return timeout; + } + + /** + * Returns the number of pending timeouts of this {@link Timer}. + */ + public long pendingTimeouts() { + return pendingTimeouts.get(); + } + + private final class Worker implements Runnable { + private final Set unprocessedTimeouts = new HashSet(); + + private long tick; + + @Override + public void run() { + // Initialize the startTime. + startTime = System.nanoTime(); + if (startTime == 0) { + // We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized. + startTime = 1; + } + + // Notify the other threads waiting for the initialization at start(). + startTimeInitialized.countDown(); + + do { + final long deadline = waitForNextTick(); + if (deadline > 0) { + int idx = (int) (tick & mask); + processCancelledTasks(); + HashedWheelBucket bucket = + wheel[idx]; + transferTimeoutsToBuckets(); + bucket.expireTimeouts(deadline); + tick++; + } + } while (workerState.get() == WORKER_STATE_STARTED); + + // Fill the unprocessedTimeouts so we can return them from stop() method. + for (HashedWheelBucket bucket : wheel) { + bucket.clearTimeouts(unprocessedTimeouts); + } + for (; ; ) { + HashedWheelTimeout timeout = timeouts.poll(); + if (timeout == null) { + break; + } + if (!timeout.isCancelled()) { + unprocessedTimeouts.add(timeout); + } + } + processCancelledTasks(); + } + + private void transferTimeoutsToBuckets() { + // transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just + // adds new timeouts in a loop. + for (int i = 0; i < 100000; i++) { + HashedWheelTimeout timeout = timeouts.poll(); + if (timeout == null) { + // all processed + break; + } + if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) { + // Was cancelled in the meantime. + continue; + } + + long calculated = timeout.deadline / tickDuration; + timeout.remainingRounds = (calculated - tick) / wheel.length; + + final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past. + int stopIndex = (int) (ticks & mask); + + HashedWheelBucket bucket = wheel[stopIndex]; + bucket.addTimeout(timeout); + } + } + + private void processCancelledTasks() { + for (; ; ) { + HashedWheelTimeout timeout = cancelledTimeouts.poll(); + if (timeout == null) { + // all processed + break; + } + try { + timeout.remove(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + /** + * calculate goal nanoTime from startTime and current tick number, + * then wait until that goal has been reached. + * + * @return Long.MIN_VALUE if received a shutdown request, + * current time otherwise (with Long.MIN_VALUE changed by +1) + */ + private long waitForNextTick() { + long deadline = tickDuration * (tick + 1); + + for (; ; ) { + final long currentTime = System.nanoTime() - startTime; + long sleepTimeMs = (deadline - currentTime + 999999) / 1000000; + + if (sleepTimeMs <= 0) { + if (currentTime == Long.MIN_VALUE) { + return -Long.MAX_VALUE; + } else { + return currentTime; + } + } + + try { + //noinspection BusyWait + Thread.sleep(sleepTimeMs); + } catch (InterruptedException ignored) { + if (workerState.get() == WORKER_STATE_SHUTDOWN) { + return Long.MIN_VALUE; + } + } + } + } + + public Set unprocessedTimeouts() { + return Collections.unmodifiableSet(unprocessedTimeouts); + } + } + + private static final class HashedWheelTimeout implements Timeout { + + private static final int ST_INIT = 0; + private static final int ST_CANCELLED = 1; + private static final int ST_EXPIRED = 2; + private static final AtomicIntegerFieldUpdater STATE_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimeout.class, "state"); + + private final HashedWheelTimer timer; + private final TimerTask task; + private final long deadline; + + @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"}) + private volatile int state = ST_INIT; + + // remainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the + // HashedWheelTimeout will be added to the correct HashedWheelBucket. + long remainingRounds; + + // This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list. + // As only the workerThread will act on it there is no need for synchronization / volatile. + HashedWheelTimeout next; + HashedWheelTimeout prev; + + // The bucket to which the timeout was added + HashedWheelBucket bucket; + + HashedWheelTimeout(HashedWheelTimer timer, TimerTask task, long deadline) { + this.timer = timer; + this.task = task; + this.deadline = deadline; + } + + @Override + public Timer timer() { + return timer; + } + + @Override + public TimerTask task() { + return task; + } + + @Override + public boolean cancel() { + // only update the state it will be removed from HashedWheelBucket on next tick. + if (!compareAndSetState(ST_INIT, ST_CANCELLED)) { + return false; + } + // If a task should be canceled we put this to another queue which will be processed on each tick. + // So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way + // we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible. + timer.cancelledTimeouts.add(this); + return true; + } + + void remove() { + HashedWheelBucket bucket = this.bucket; + if (bucket != null) { + bucket.remove(this); + } else { + timer.pendingTimeouts.decrementAndGet(); + } + } + + public boolean compareAndSetState(int expected, int state) { + return STATE_UPDATER.compareAndSet(this, expected, state); + } + + public int state() { + return state; + } + + @Override + public boolean isCancelled() { + return state() == ST_CANCELLED; + } + + @Override + public boolean isExpired() { + return state() == ST_EXPIRED; + } + + public void expire() { + if (!compareAndSetState(ST_INIT, ST_EXPIRED)) { + return; + } + + try { + task.run(this); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + @Override + public String toString() { + final long currentTime = System.nanoTime(); + long remaining = deadline - currentTime + timer.startTime; + + StringBuilder buf = new StringBuilder(192) + .append("HashedWheelTimer(deadline: "); + if (remaining > 0) { + buf.append(remaining) + .append(" ns later"); + } else if (remaining < 0) { + buf.append(-remaining) + .append(" ns ago"); + } else { + buf.append("now"); + } + + if (isCancelled()) { + buf.append(", cancelled"); + } + + return buf.append(", task: ") + .append(task()) + .append(')') + .toString(); + } + } + + /** + * Bucket that stores HashedWheelTimeouts. These are stored in a linked-list like datastructure to allow easy + * removal of HashedWheelTimeouts in the middle. Also the HashedWheelTimeout act as nodes themself and so no + * extra object creation is needed. + */ + private static final class HashedWheelBucket { + // Used for the linked-list datastructure + private HashedWheelTimeout head; + private HashedWheelTimeout tail; + + /** + * Add {@link HashedWheelTimeout} to this bucket. + */ + public void addTimeout(HashedWheelTimeout timeout) { + assert timeout.bucket == null; + timeout.bucket = this; + if (head == null) { + head = tail = timeout; + } else { + tail.next = timeout; + timeout.prev = tail; + tail = timeout; + } + } + + /** + * Expire all {@link HashedWheelTimeout}s for the given {@code deadline}. + */ + public void expireTimeouts(long deadline) { + HashedWheelTimeout timeout = head; + + // process all timeouts + while (timeout != null) { + HashedWheelTimeout next = timeout.next; + if (timeout.remainingRounds <= 0) { + next = remove(timeout); + if (timeout.deadline <= deadline) { + timeout.expire(); + } else { + // The timeout was placed into a wrong slot. This should never happen. + throw new IllegalStateException(String.format( + "timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline)); + } + } else if (timeout.isCancelled()) { + next = remove(timeout); + } else { + timeout.remainingRounds--; + } + timeout = next; + } + } + + public HashedWheelTimeout remove(HashedWheelTimeout timeout) { + HashedWheelTimeout next = timeout.next; + // remove timeout that was either processed or cancelled by updating the linked-list + if (timeout.prev != null) { + timeout.prev.next = next; + } + if (timeout.next != null) { + timeout.next.prev = timeout.prev; + } + + if (timeout == head) { + // if timeout is also the tail we need to adjust the entry too + if (timeout == tail) { + tail = null; + head = null; + } else { + head = next; + } + } else if (timeout == tail) { + // if the timeout is the tail modify the tail to be the prev node. + tail = timeout.prev; + } + // null out prev, next and bucket to allow for GC. + timeout.prev = null; + timeout.next = null; + timeout.bucket = null; + timeout.timer.pendingTimeouts.decrementAndGet(); + return next; + } + + /** + * Clear this bucket and return all not expired / cancelled {@link Timeout}s. + */ + public void clearTimeouts(Set set) { + for (; ; ) { + HashedWheelTimeout timeout = pollTimeout(); + if (timeout == null) { + return; + } + if (timeout.isExpired() || timeout.isCancelled()) { + continue; + } + set.add(timeout); + } + } + + private HashedWheelTimeout pollTimeout() { + HashedWheelTimeout head = this.head; + if (head == null) { + return null; + } + HashedWheelTimeout next = head.next; + if (next == null) { + tail = this.head = null; + } else { + this.head = next; + next.prev = null; + } + + // null out prev and next to allow for GC. + head.next = null; + head.prev = null; + head.bucket = null; + return head; + } + } +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java new file mode 100644 index 0000000..84e875c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timeout.java @@ -0,0 +1,37 @@ +package com.jd.platform.async.openutil.timer; + +/** + * 借鉴netty。 + * 一个连接着{@link Timer}和{@link TimerTask},表示着任务状态的“关系类” + * + * @author create by TcSnZh on 2021/5/9-下午6:33 + */ +public interface Timeout { + /** + * 返回对应的{@link Timer}。 + */ + Timer timer(); + + /** + * 返回对应的{@link TimerTask} + */ + TimerTask task(); + + /** + * 当且仅当关联的{@link TimerTask}已超时时,才返回{@code true}。 + */ + boolean isExpired(); + + /** + * 当且仅当关联的{@link TimerTask}被取消时,才返回{@code true}。 + */ + boolean isCancelled(); + + /** + * 尝试取消关联的{@link TimerTask}。如果任务已经执行或已取消,它将无副作用地返回。 + * + * @return 如果取消成功完成,则为true,否则为false + */ + @SuppressWarnings("unused") + boolean cancel(); +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java new file mode 100644 index 0000000..17870c1 --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/Timer.java @@ -0,0 +1,43 @@ +package com.jd.platform.async.openutil.timer; + +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 照抄netty + * 让{@link TimerTask}在后台线程中执行。 + * + * @author create by TcSnZh on 2021/5/9-下午6:33 + */ +public interface Timer { + + /** + * 使{@link TimerTask}在指定的延迟后执行一次。 + * + * @param delay 延时长度 + * @param unit 延时单位 + * @return 返回 {@link Timeout}关系类 + * @throws IllegalStateException 如果此计时器已经已停止 + * @throws RejectedExecutionException 如果挂起的超时太多,则创建新的超时会导致系统不稳定。 + */ + Timeout newTimeout(TimerTask task, long delay, TimeUnit unit); + + @SuppressWarnings("unused") + default Timeout newTimeout(Runnable runnable, long delay, TimeUnit unit) { + AtomicReference timeoutRef = new AtomicReference<>(); + newTimeout(timeout -> { + timeoutRef.set(timeout); + runnable.run(); + }, delay, unit); + return timeoutRef.get(); + } + + /** + * 释放此{@link Timer}所有资源(例如线程),并取消所有尚未执行的任务。 + * + * @return 与被该方法取消的任务相关联的 {@link Timeout} + */ + Set stop(); +} diff --git a/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java new file mode 100644 index 0000000..047590c --- /dev/null +++ b/asyncTool-openutil/src/main/java/com/jd/platform/async/openutil/timer/TimerTask.java @@ -0,0 +1,10 @@ +package com.jd.platform.async.openutil.timer; + +/** + * 类似于netty的TimerTask。 + * + * @author create by TcSnZh on 2021/5/9-下午5:17 + */ +public interface TimerTask{ + void run(Timeout timeout) throws Exception; +} diff --git a/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java b/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java new file mode 100644 index 0000000..c212ad3 --- /dev/null +++ b/asyncTool-openutil/src/test/java/openutiltest/PrintProxy.java @@ -0,0 +1,51 @@ +package openutiltest; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 便于测试的jdk动态代理 + * + * @author create by TcSnZh on 2021/5/16-下午11:38 + */ +public class PrintProxy { + public PrintProxy(Class clazz) { + this.interfaceClazz = clazz; + } + + private final Class interfaceClazz; + + public I proxyTo(I obj, String objNickName) { + //noinspection unchecked + return (I) Proxy.newProxyInstance( + obj.getClass().getClassLoader(), + new Class[]{interfaceClazz}, + (proxy, method, args) -> { + String methodInfo = methodInfo(method); + try { + Object res = method.invoke(obj, args); + System.out.printf(objNickName + " 执行方法: %-40s --> 方法返回值: %-20s --> this.toString() = %-40s\n", + methodInfo, res, obj); + return res; + } catch (Exception e) { + System.err.printf(objNickName + " 执行方法: %-40s --> 异常信息: %-40s --> this.toString() = %-40s\n", + methodInfo, e.getClass().getSimpleName() + " : " + e.getMessage(), obj + ); + throw e; + } + } + ); + } + + private static String methodInfo(Method method) { + StringBuilder sb = new StringBuilder().append(method.getName()).append('('); + for (Class parameterType : method.getParameterTypes()) { + sb.append(parameterType.getSimpleName()).append(", "); + } + if (method.getParameterTypes().length > 0) { + sb.delete(sb.length() - 2, sb.length()); + } + return sb.append(')').toString(); + } + +} diff --git a/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java new file mode 100644 index 0000000..bddf07b --- /dev/null +++ b/asyncTool-openutil/src/test/java/openutiltest/TestGraph.java @@ -0,0 +1,37 @@ +package openutiltest; + +import com.jd.platform.async.openutil.collection.CommonDirectedGraph; +import com.jd.platform.async.openutil.collection.DirectedGraph; + +import java.util.Arrays; + +/** + * 测试图工具类的使用 + * + * @author create by TcSnZh on 2021/5/16-下午11:25 + */ +class TestGraph { + public static void main(String[] args) { + test_CommonDirectedGraph(); + } + + private static void test_CommonDirectedGraph() { + System.out.println("\n\n ==================== 测试正常使用 =================="); + //noinspection unchecked + DirectedGraph graph = + new PrintProxy<>(DirectedGraph.class).proxyTo(new CommonDirectedGraph<>(), "graph"); + graph.isDirected(); + graph.addNode("胖虎"); + graph.addNode("大雄"); + graph.putRelation("胖虎", "打", "大雄"); + graph.addNode("静香"); + graph.nodesView().addAll(Arrays.asList("小夫", "胖虎的妹妹", "哆啦A梦")); + graph.putRelation("胖虎", "是其哥", "胖虎的妹妹"); + graph.putRelation("胖虎的妹妹", "是其妹", "胖虎"); + graph.putRelation("胖虎的妹妹", "喜欢", "大雄"); + graph.putRelation("胖虎", "????", "小夫"); + graph.putRelation("大雄", "喜欢", "静香"); + graph.removeNode("大雄"); + graph.getRelations(); + } +} diff --git a/pom.xml b/pom.xml index 6459b99..cdb66dc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,8 @@ com.jd.platform asyncTool - 1.4.1-SNAPSHOT + pom + 1.5.1-SNAPSHOT @@ -21,4 +22,8 @@ + + asyncTool-openutil + asyncTool-core + \ No newline at end of file diff --git a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java b/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java deleted file mode 100755 index fe074bd..0000000 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java +++ /dev/null @@ -1,720 +0,0 @@ -package com.jd.platform.async.wrapper; - -import com.jd.platform.async.callback.DefaultCallback; -import com.jd.platform.async.callback.ICallback; -import com.jd.platform.async.callback.IWorker; -import com.jd.platform.async.exception.SkippedException; -import com.jd.platform.async.executor.timer.SystemClock; -import com.jd.platform.async.worker.*; -import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy; -import com.jd.platform.async.wrapper.actionstrategy.DependMustStrategyMapper; -import com.jd.platform.async.wrapper.actionstrategy.DependWrapperStrategyMapper; -import com.jd.platform.async.wrapper.actionstrategy.DependenceAction; -import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 对每个worker及callback进行包装,一对一 - *

- * v1.5时将其抽取为抽象类,以解耦并提高扩展性。 - * - * @author wuweifeng wrote on 2019-11-19. - */ -public abstract class WorkerWrapper { - /** - * 该wrapper的唯一标识 - */ - protected final String id; - /** - * worker将来要处理的param - */ - protected T param; - protected IWorker worker; - protected ICallback callback; - /** - * 标记该事件是否已经被处理过了,譬如已经超时返回false了,后续rpc又收到返回值了,则不再二次回调 - * 经试验,volatile并不能保证"同一毫秒"内,多线程对该值的修改和拉取 - *

- * 1-finish, 2-error, 3-working - */ - protected final AtomicInteger state = new AtomicInteger(0); - /** - * 也是个钩子变量,用来存临时的结果 - */ - protected volatile WorkResult workResult = WorkResult.defaultResult(); - /** - * 该map存放所有wrapper的id和wrapper映射 - *

- * 需要线程安全。 - */ - private Map> forParamUseWrappers; - /** - * 各种策略的封装类。 - *

- * 其实是因为加功能太多导致这个对象大小超过了128Byte,所以强迫症的我不得不把几个字段丢到策略类里面去。 - * ps: 大小超过128Byte令我(TcSnZh)难受的一比,就像走在草坪的格子上,一步嫌小、两步扯蛋。 - * IDEA可以使用JOL Java Object Layout插件查看对象大小。 - */ - private final WrapperStrategy wrapperStrategy = new WrapperStrategy(); - /** - * 超时检查,该值允许为null。表示不设置。 - */ - private volatile TimeOutProperties timeOutProperties; - - // ***** state属性的常量值 ***** - - public static final int FINISH = 1; - public static final int ERROR = 2; - public static final int WORKING = 3; - public static final int INIT = 0; - - WorkerWrapper(String id, IWorker worker, T param, ICallback callback) { - if (worker == null) { - throw new NullPointerException("async.worker is null"); - } - this.worker = worker; - this.param = param; - this.id = id; - //允许不设置回调 - if (callback == null) { - callback = new DefaultCallback<>(); - } - this.callback = callback; - } - - // ========== public ========== - - /** - * 外部调用本线程运行此Wrapper的入口方法。 - * - * @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。 - * @param remainTime 剩下的时间 - * @param forParamUseWrappers 用于保存经过的wrapper的信息的Map,key为id。 - * @param inspector wrapper调度检查器 - */ - public void work(ExecutorService executorService, - long remainTime, - Map> forParamUseWrappers, - WrapperEndingInspector inspector) { - work(executorService, null, remainTime, forParamUseWrappers, inspector); - } - - public String getId() { - return id; - } - - public WorkResult getWorkResult() { - return workResult; - } - - public void setParam(T param) { - this.param = param; - } - - public int getState() { - return state.get(); - } - - /** - * 获取之后的下游Wrapper - */ - public abstract Set> getNextWrappers(); - - /** - * 使wrapper状态修改为超时失败。(但如果已经执行完成则不会修改) - *

- * 本方法不会试图执行超时判定逻辑。 - * 如果要执行超时逻辑判断,请用{@link TimeOutProperties#checkTimeOut(boolean)}并传入参数true。 - */ - public void failNow() { - int state = getState(); - if (state == INIT || state == WORKING) { - fastFail(state, null); - } - } - - public WrapperStrategy getWrapperStrategy() { - return wrapperStrategy; - } - - // ========== protected ========== - - /** - * 快速失败 - * - * @return 已经失败则返回false,如果刚才设置为失败了则返回true。 - */ - protected boolean fastFail(int expect, Exception e) { - //试图将它从expect状态,改成Error - if (!compareAndSetState(expect, ERROR)) { - return false; - } - - //尚未处理过结果 - if (checkIsNullResult()) { - if (e == null) { - workResult.setResultState(ResultState.TIMEOUT); - } else { - workResult.setResultState(ResultState.EXCEPTION); - workResult.setEx(e); - } - workResult.setResult(worker.defaultValue()); - } - callback.result(false, param, workResult); - return true; - } - - /** - * 判断{@link #state}状态是否是初始值。 - */ - protected boolean checkIsNullResult() { - return ResultState.DEFAULT == workResult.getResultState(); - } - - protected boolean compareAndSetState(int expect, int update) { - return this.state.compareAndSet(expect, update); - } - - /** - * 工作的核心方法。 - * - * @param fromWrapper 代表这次work是由哪个上游wrapper发起的。如果是首个Wrapper则为null。 - * @param remainTime 剩余时间。 - */ - protected void work(ExecutorService executorService, - WorkerWrapper fromWrapper, - long remainTime, - Map> forParamUseWrappers, - WrapperEndingInspector inspector) { - this.setForParamUseWrappers(forParamUseWrappers); - //将自己放到所有wrapper的集合里去 - forParamUseWrappers.put(id, this); - long now = SystemClock.now(); - //总的已经超时了,就快速失败,进行下一个 - if (remainTime <= 0) { - fastFail(INIT, null); - beginNext(executorService, now, remainTime, inspector); - return; - } - //如果自己已经执行过了。 - //可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 - if (getState() == FINISH || getState() == ERROR) { - beginNext(executorService, now, remainTime, inspector); - return; - } - - // 判断是否要跳过自己,该方法可能会跳过正在工作的自己。 - final WrapperStrategy wrapperStrategy = getWrapperStrategy(); - if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) { - fastFail(INIT, new SkippedException()); - beginNext(executorService, now, remainTime, inspector); - return; - } - - //如果没有任何依赖,说明自己就是第一批要执行的 - final Set> dependWrappers = getDependWrappers(); - if (dependWrappers == null || dependWrappers.size() == 0) { - fire(); - beginNext(executorService, now, remainTime, inspector); - return; - } - - DependenceAction.WithProperty judge = wrapperStrategy.judgeAction(dependWrappers, this, fromWrapper); - - switch (judge.getDependenceAction()) { - case TAKE_REST: - inspector.reduceWrapper(this); - return; - case FAST_FAIL: - switch (judge.getResultState()) { - case TIMEOUT: - fastFail(INIT, null); - break; - case EXCEPTION: - fastFail(INIT, judge.getFastFailException()); - break; - default: - fastFail(INIT, new RuntimeException("ResultState " + judge.getResultState() + " set to FAST_FAIL")); - break; - } - beginNext(executorService, now, remainTime, inspector); - break; - case START_WORK: - fire(); - beginNext(executorService, now, remainTime, inspector); - break; - case JUDGE_BY_AFTER: - default: - inspector.reduceWrapper(this); - throw new IllegalStateException("策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + ",fromWrapper=" + fromWrapper); - } - } - - /** - * 进行下一个任务 - */ - protected void beginNext(ExecutorService executorService, long now, long remainTime, WrapperEndingInspector inspector) { - //花费的时间 - final long costTime = SystemClock.now() - now; - final long nextRemainTIme = remainTime - costTime; - Set> nextWrappers = getNextWrappers(); - if (nextWrappers == null) { - inspector.setWrapperEndWithTryPolling(this); - return; - } - // nextWrappers只有一个,就用本线程继续跑。 - if (nextWrappers.size() == 1) { - try { - WorkerWrapper next = nextWrappers.stream().findFirst().get(); - inspector.addWrapper(next); - next.work(executorService, WorkerWrapper.this, nextRemainTIme, getForParamUseWrappers(), inspector); - } finally { - inspector.setWrapperEndWithTryPolling(this); - } - return; - } - // nextWrappers有多个 - try { - inspector.addWrapper(nextWrappers); - nextWrappers.forEach(next -> executorService.submit(() -> - next.work(executorService, this, nextRemainTIme, getForParamUseWrappers(), inspector)) - ); - } finally { - inspector.setWrapperEndWithTryPolling(this); - } - } - - /** - * 本工作线程执行自己的job.判断阻塞超时这里开始时会判断一次总超时时间,但在轮询线程会判断单个wrapper超时时间,并也会判断总超时时间。 - */ - protected void fire() { - //阻塞取结果 - //避免重复执行 - if (!checkIsNullResult()) { - return; - } - try { - //如果已经不是init状态了,说明正在被执行或已执行完毕。这一步很重要,可以保证任务不被重复执行 - if (!compareAndSetState(INIT, WORKING)) { - return; - } - V resultValue; - try { - callback.begin(); - if (timeOutProperties != null) { - timeOutProperties.startWorking(); - } - //执行耗时操作 - resultValue = (V) worker.action(param, (Map) getForParamUseWrappers()); - } finally { - if (timeOutProperties != null) { - timeOutProperties.endWorking(); - } - } - //如果状态不是在working,说明别的地方已经修改了 - if (!compareAndSetState(WORKING, FINISH)) { - return; - } - workResult.setResultState(ResultState.SUCCESS); - workResult.setResult(resultValue); - //回调成功 - callback.result(true, param, workResult); - } catch (Exception e) { - //避免重复回调 - if (!checkIsNullResult()) { - return; - } - fastFail(WORKING, e); - } - } - - // ========== hashcode and equals ========== - - @Override - public boolean equals(Object o) { - return super.equals(o); - } - - /** - * {@code return id.hashCode();}返回id值的hashcode - */ - @Override - public int hashCode() { - // final String id can use to .hashcode() . - return id.hashCode(); - } - - // ========== Builder ========== - - public static WorkerWrapperBuilder builder() { - return new Builder<>(); - } - - /** - * 自v1.5,该类被抽取到{@link StableWorkerWrapperBuilder}抽象类,兼容之前的版本。 - */ - public static class Builder extends StableWorkerWrapperBuilder> { - /** - * @deprecated 建议使用 {@link #builder()}返回{@link WorkerWrapperBuilder}接口,以调用v1.5之后的规范api - */ - @Deprecated - public Builder() { - } - } - - // ========== package access methods , for example , some getter/setter that doesn't want to be public ========== - - T getParam() { - return param; - } - - IWorker getWorker() { - return worker; - } - - void setWorker(IWorker worker) { - this.worker = worker; - } - - ICallback getCallback() { - return callback; - } - - void setCallback(ICallback callback) { - this.callback = callback; - } - - void setState(int state) { - this.state.set(state); - } - - Map> getForParamUseWrappers() { - return forParamUseWrappers; - } - - void setForParamUseWrappers(Map> forParamUseWrappers) { - this.forParamUseWrappers = forParamUseWrappers; - } - - void setWorkResult(WorkResult workResult) { - this.workResult = workResult; - } - - abstract void setNextWrappers(Set> nextWrappers); - - abstract Set> getDependWrappers(); - - abstract void setDependWrappers(Set> dependWrappers); - - TimeOutProperties getTimeOut() { - return timeOutProperties; - } - - void setTimeOut(TimeOutProperties timeOutProperties) { - this.timeOutProperties = timeOutProperties; - } - - // ========== toString ========== - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(200) - .append("WorkerWrapper{id=").append(id) - .append(", param=").append(param) - .append(", worker=").append(worker) - .append(", callback=").append(callback) - .append(", state="); - int state = this.state.get(); - if (state == FINISH) { - sb.append("FINISH"); - } else if (state == WORKING) { - sb.append("WORKING"); - } else if (state == INIT) { - sb.append("INIT"); - } else if (state == ERROR) { - sb.append("ERROR"); - } else { - throw new IllegalStateException("unknown state : " + state); - } - sb - .append(", workResult=").append(workResult) - // 防止循环引用,这里只输出相关Wrapper的id - .append(", forParamUseWrappers::getId=["); - getForParamUseWrappers().keySet().forEach(wrapperId -> sb.append(wrapperId).append(", ")); - if (getForParamUseWrappers().keySet().size() > 0) { - sb.delete(sb.length() - 2, sb.length()); - } - sb - .append("], dependWrappers::getId=["); - getDependWrappers().stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", ")); - if (getDependWrappers().size() > 0) { - sb.delete(sb.length() - 2, sb.length()); - } - sb - .append("], nextWrappers::getId=["); - getNextWrappers().stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", ")); - if (getNextWrappers().size() > 0) { - sb.delete(sb.length() - 2, sb.length()); - } - sb - .append("]") - .append(", wrapperStrategy=").append(getWrapperStrategy()) - .append(", timeOutProperties=").append(getTimeOut()) - .append('}'); - return sb.toString(); - } - - public static class WrapperStrategy implements DependenceStrategy, SkipStrategy { - - // ========== 这三个属性用来判断是否要开始工作 ========== - - // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy - - /** - * 对特殊Wrapper专用的依赖响应策略。 - * 该值允许为null - */ - private DependWrapperStrategyMapper dependWrapperStrategyMapper; - /** - * 对必须完成的(must的)Wrapper的依赖响应策略。 - * 该值允许为null - *

- * 这是一个不得不向历史妥协的属性。用于适配must开关方式。 - */ - private DependMustStrategyMapper dependMustStrategyMapper; - /** - * 依赖响应全局策略。 - */ - private DependenceStrategy dependenceStrategy; - - @Override - public DependenceAction.WithProperty judgeAction(Set> dependWrappers, - WorkerWrapper thisWrapper, - WorkerWrapper fromWrapper) { - // 如果存在依赖,则调用三层依赖响应策略进行判断 - DependenceStrategy strategy = dependWrapperStrategyMapper; - if (dependMustStrategyMapper != null) { - strategy = strategy == null ? dependMustStrategyMapper : strategy.thenJudge(dependMustStrategyMapper); - } - if (dependenceStrategy != null) { - strategy = strategy == null ? dependenceStrategy : strategy.thenJudge(dependenceStrategy); - } - if (strategy == null) { - throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!"); - } - return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper); - } - - public DependWrapperStrategyMapper getDependWrapperStrategyMapper() { - return dependWrapperStrategyMapper; - } - - public void setDependWrapperStrategyMapper(DependWrapperStrategyMapper dependWrapperStrategyMapper) { - this.dependWrapperStrategyMapper = dependWrapperStrategyMapper; - } - - public DependMustStrategyMapper getDependMustStrategyMapper() { - return dependMustStrategyMapper; - } - - public void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) { - this.dependMustStrategyMapper = dependMustStrategyMapper; - } - - public DependenceStrategy getDependenceStrategy() { - return dependenceStrategy; - } - - public void setDependenceStrategy(DependenceStrategy dependenceStrategy) { - this.dependenceStrategy = dependenceStrategy; - } - - // ========== 跳过策略 ========== - - private SkipStrategy skipStrategy; - - @Override - public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - return skipStrategy != null && skipStrategy.shouldSkip(nextWrappers, thisWrapper, fromWrapper); - } - - public SkipStrategy getSkipStrategy() { - return skipStrategy; - } - - public void setSkipStrategy(SkipStrategy skipStrategy) { - this.skipStrategy = skipStrategy; - } - - // ========== toString ========== - - @Override - public String toString() { - return "WrapperStrategy{" + - "dependWrapperStrategyMapper=" + dependWrapperStrategyMapper + - ", dependMustStrategyMapper=" + dependMustStrategyMapper + - ", dependenceStrategy=" + dependenceStrategy + - ", skipStrategy=" + skipStrategy + - '}'; - } - } - - public static class TimeOutProperties { - private final boolean enable; - private final long time; - private final TimeUnit unit; - private final boolean allowInterrupt; - private final WorkerWrapper wrapper; - - private final Object lock = new Object(); - - private volatile boolean started = false; - private volatile boolean ended = false; - private volatile long startWorkingTime; - private volatile long endWorkingTime; - private volatile Thread doWorkingThread; - - public TimeOutProperties(boolean enable, long time, TimeUnit unit, boolean allowInterrupt, WorkerWrapper wrapper) { - this.enable = enable; - this.time = time; - this.unit = unit; - this.allowInterrupt = allowInterrupt; - this.wrapper = wrapper; - } - - // ========== 工作线程调用 ========== - - public void startWorking() { - synchronized (lock) { - started = true; - startWorkingTime = SystemClock.now(); - doWorkingThread = Thread.currentThread(); - } - } - - public void endWorking() { - synchronized (lock) { - ended = true; - doWorkingThread = null; - endWorkingTime = SystemClock.now(); - } - } - - // ========== 轮询线程调用 ========== - - /** - * 检查超时。 - * 可以将boolean参数传入true以在超时的时候直接失败。 - * - * @param withStop 如果为false,不会发生什么,仅仅是单纯的判断是否超时。 - * 如果为true,则会去快速失败wrapper{@link #failNow()},有必要的话还会打断线程。 - * @return 如果 超时 或 执行时间超过限制 返回true;未超时返回false。 - */ - public boolean checkTimeOut(boolean withStop) { - if (enable) { - synchronized (lock) { - if (started) { - // 判断执行中的wrapper是否超时 - long dif = (ended ? endWorkingTime : SystemClock.now()) - startWorkingTime; - if (dif > unit.toMillis(time)) { - if (withStop) { - if (allowInterrupt) { - doWorkingThread.interrupt(); - } - wrapper.failNow(); - ended = true; - } - return true; - } - return false; - } - } - } - return false; - } - - // ========== package ========== - - boolean isEnable() { - return enable; - } - - long getTime() { - return time; - } - - TimeUnit getUnit() { - return unit; - } - - boolean isAllowInterrupt() { - return allowInterrupt; - } - - Object getLock() { - return lock; - } - - boolean isStarted() { - return started; - } - - void setStarted(boolean started) { - this.started = started; - } - - boolean isEnded() { - return ended; - } - - void setEnded(boolean ended) { - this.ended = ended; - } - - long getStartWorkingTime() { - return startWorkingTime; - } - - void setStartWorkingTime(long startWorkingTime) { - this.startWorkingTime = startWorkingTime; - } - - long getEndWorkingTime() { - return endWorkingTime; - } - - void setEndWorkingTime(long endWorkingTime) { - this.endWorkingTime = endWorkingTime; - } - - Thread getDoWorkingThread() { - return doWorkingThread; - } - - void setDoWorkingThread(Thread doWorkingThread) { - this.doWorkingThread = doWorkingThread; - } - - - // ========== toString ========== - - @Override - public String toString() { - return "TimeOutProperties{" + - "enable=" + enable + - ", time=" + time + - ", unit=" + unit + - ", allowInterrupt=" + allowInterrupt + - ", wrapper::getId=" + wrapper.getId() + - ", started=" + started + - ", ended=" + ended + - ", startWorkingTime=" + startWorkingTime + - ", endWorkingTime=" + endWorkingTime + - ", doWorkingThread=" + doWorkingThread + - '}'; - } - } -} diff --git a/src/main/java/com/jd/platform/async/wrapper/WrapperEndingInspector.java b/src/main/java/com/jd/platform/async/wrapper/WrapperEndingInspector.java deleted file mode 100644 index 66bb3b9..0000000 --- a/src/main/java/com/jd/platform/async/wrapper/WrapperEndingInspector.java +++ /dev/null @@ -1,486 +0,0 @@ -package com.jd.platform.async.wrapper; - -import com.jd.platform.async.executor.timer.SystemClock; -import com.jd.platform.async.worker.WorkResult; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; - - -/** - * 判断{@link WorkerWrapper}是否链路调用完成的轮询器。 - * ================================================================================= - *

- * 在v1.4及以前的版本,存在如下问题: - * > - * 在使用线程数量较少的线程池进行beginWork时,调用WorkerWrapper#beginNext方法时, - * 会因为本线程等待下游Wrapper执行完成而存在线程耗尽bug。线程池会死翘翘的僵住、动弹不得。 - * > - * 例如仅有2个线程的线程池,执行以下任务: - * {@code - *

- * 这是旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况,在test/v15.dependnew中示例testThreadPolling_V14Bug说明了这个bug - * 线程数:2 - * A(5ms)--B1(10ms) ---|--> C1(5ms) - * . \ | (B1、B2全部完成可执行C1、C2) - * . ---> B2(20ms) --|--> C2(5ms) - *

- * } - * 线程1执行了A,然后在{@link java.util.concurrent.CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。 - * 线程2执行了B1或B2中的一个,也在allOf方法等待C1、C2完成。 - * 结果没有线程执行C和B2了,导致超时而死,并且这个线程池线程有可能被耗尽。 - * > - * v1.5的解决方案是,放弃使工作线程遭致阻塞的{@link java.util.concurrent.CompletableFuture}, - * 而是让工作线程在工作前注册到本“完成检查器”{@link WrapperEndingInspector},然后交由轮询中心{@link PollingCenter}进行检查是否完成。 - *

- * ================================================================================= - *

- * 本类的工作原理: - * . - * 原理: - * (1)首先在Async代码中,将主动运行的wrapper都保存到一个inspector{@link #addWrapper(WorkerWrapper)}, - * (2)主动运行的wrapper于FINISH/ERROR时,先异步submit所有下游wrapper,在其执行时将自身(下游wrapper)保存到inspector, - * (3)然后在异步submit完所有下游wrapper后,将调用{@link #setWrapperEndWithTryPolling(WorkerWrapper)}方法, - * . 设置自己的{@link #wrappers}为true,并呼叫轮询{@link PollingCenter#tryPolling()}。 - * (4)在下游wrapper中,经过策略器判断后, - * . 若是不需要运行,则把本wrapper计数-1{@link WrapperNode#count},若是计数<1则将{@link WrapperNode}移出{@link #wrappers}。 - * . 若是需要运行,则运行之,然后跳转到 (2) 的情节。如此递归,执行链路上所有需要执行的wrapper最后都会存在于{@link #wrappers}中。 - * . - * 因此,若是存在任一其{@link WrapperNode#called}为false的wrapper,则表示这条链路还没有调用完。 - * 若是在{@link #wrappers}中所有的{@link WrapperNode#called}为true时,即可判断出链路执行完毕了。 - *

- * - * @author create by TcSnZh on 2021/5/5-下午3:22 - */ -public class WrapperEndingInspector implements Comparable { - /** - * 最迟完成时间 - */ - private final long latestFinishTime; - - /** - * 保存 需要检查的wrapper--相关属性 的Map。 - */ - private final ConcurrentHashMap wrappers = new ConcurrentHashMap<>(); - - /** - * 当全部wrapper都调用结束,它会countDown - */ - private final CountDownLatch endCDL = new CountDownLatch(1); - - /** - * 读锁用于修改数据,写锁用于轮询。使用公平锁让wrapper的时间波动不会太长。 - *

- * 在轮询到本inspector时,之所以要上写锁,是因为: - * 假如此时有个Wrapper正在调用{@link #addWrapper(WorkerWrapper)},则wrappers发生了改变。 - * 假如现在恰巧访问到的是{@link #wrappers}迭代器的最后一个,但此时又加入了另一个,且这另一个又是需要去执行的。 - * 那么假如在迭代器遍历到目前访问到的wrapper都是呼叫完毕的,那么这新加入的一个就会被忽略,从而判定为全部完成。致使bug发生。 - *

- * 此外,即便轮询时上写锁,对性能的影响也是有限的。因为这只会在“呼叫别人”的时候发生工作线程与轮询线程的锁争抢, - * 而在工作线程执行{@link com.jd.platform.async.callback.IWorker#action(Object, Map)}或 - * {@link com.jd.platform.async.callback.ICallback#result(boolean, Object, WorkResult)}时,并不会与轮询线程去 - * 争抢锁,而通常这个工作的时间才是最耗时的。 - */ - private final ReentrantReadWriteLock modifyPollingLock = new ReentrantReadWriteLock(true); - - /** - * 当轮询发现超时时,该值被设为false - */ - private final AtomicBoolean haveNotTimeOut = new AtomicBoolean(true); - - public WrapperEndingInspector(long latestFinishTime) { - this.latestFinishTime = latestFinishTime; - } - - public void registerToPollingCenter() { - modifyPollingLock.readLock().lock(); - try { - // 不重复put,以免InspectorNode被替换为另一个 - PollingCenter.getInstance().inspectionMap.putIfAbsent(this, new PollingCenter.InspectorNode()); - } finally { - modifyPollingLock.readLock().unlock(); - } - } - - public void addWrapper(WorkerWrapper wrapper) { - modifyPollingLock.readLock().lock(); - try { - wrappers.computeIfAbsent(wrapper, k -> new WrapperNode()).count.incrementAndGet(); - } finally { - modifyPollingLock.readLock().unlock(); - } - } - - public void addWrapper(Collection wrappers) { - modifyPollingLock.readLock().lock(); - try { - Objects.requireNonNull(wrappers).forEach(this::addWrapper); - } finally { - modifyPollingLock.readLock().unlock(); - } - } - - public void reduceWrapper(WorkerWrapper wrapper) { - modifyPollingLock.readLock().lock(); - try { - /* - * 有可能发生这情况,一个Wrapper刚被加进去,执行了零/一/多次,均不满足执行条件,但是下次调用却应当使其启动。 - */ - if (wrapper.getState() != WorkerWrapper.INIT) { - final WrapperNode wrapperNode = wrappers.get(wrapper); - if (wrapperNode == null) { - return; - } - synchronized (wrapperNode) { - if (wrapperNode.count.decrementAndGet() < 1) { - wrappers.remove(wrapper); - } - } - } - } finally { - modifyPollingLock.readLock().unlock(); - } - } - - /** - * 原子的设置这个Wrapper已经呼叫完成了。 - *

- * 该方法会调用{@link PollingCenter#tryPolling()},呼叫轮询线程 - * - * @return 如果为true,表示设置成功。为false表示已经被设置过了。 - */ - public boolean setWrapperEndWithTryPolling(WorkerWrapper wrapper) { - modifyPollingLock.readLock().lock(); - try { - return !wrappers.get(wrapper).called.getAndSet(true); - } finally { - modifyPollingLock.readLock().unlock(); - PollingCenter.getInstance().tryPolling(); - } - } - - /** - * 供外部调用的等待方法 - * - * @return 在超时前完成,返回true。超时时间一到,就会返回false。就像,人被杀,就会死。 - * @throws InterruptedException 外部调用的当前线程被中断时,会抛出这个异常。 - */ - public boolean await() throws InterruptedException { - endCDL.await(); - return haveNotTimeOut.get(); - } - - /** - * {@link PollingCenter}会优先把最迟完成时间(即开始时间+超时时间)较早的Inspection放在前面。 - */ - @Override - public int compareTo(WrapperEndingInspector other) { - if (this.latestFinishTime - other.latestFinishTime < 0) { - return -1; - } - return 1; - } - - @Override - public String toString() { - return "WrapperEndingInspector{" + - "remainTime=" + (latestFinishTime - SystemClock.now()) + - ", wrappers=" + - wrappers.entrySet().stream() - .collect(Collectors.toMap(entry -> entry.getKey().getId(), Map.Entry::getValue)) - + - ", endCDL.getCount()=" + endCDL.getCount() + - ", writePollingLock={read=" + modifyPollingLock.getReadLockCount() + ",write=" + modifyPollingLock.getWriteHoldCount() + - "} }"; - } - - /** - * 节点对象,保存属性信息于{@link #wrappers}中。 - *

- * 当试图把Node移出本Map时,该Node对象自身将会被上锁。 - */ - public static class WrapperNode { - /** - * 是否已经呼叫完了下游wrapper - */ - AtomicBoolean called = new AtomicBoolean(false); - /** - * 本wrapper总共被呼叫次数的统计。若小于1则会被移出map。 - */ - AtomicInteger count = new AtomicInteger(0); - - @Override - public String toString() { - return "{" + - "called=" + called.get() + - ", count=" + count.get() + - '}'; - } - } - - /** - * 轮询中心。具体的轮询调度由其完成。 - *

- * {@link #registerToPollingCenter()}调用时,就会将inspector注册到本轮询中心以供轮询。 - */ - public static class PollingCenter { - public static class InspectorNode { - /** - * 延迟轮询时间戳。 - */ - private volatile long delayTimeStamp = Long.MAX_VALUE; - - private final ReadWriteLock lockOfDelayTimeStamp = new ReentrantReadWriteLock(); - - /** - * 比较传入时间戳与{@link #delayTimeStamp},并设置小的那个为{@link #delayTimeStamp}的值。 - * - * @param otherDelayTimeStamp 试图用来比较的另一个时间戳 - */ - public void compareAndSetMinDelayTimeStamp(long otherDelayTimeStamp) { - lockOfDelayTimeStamp.writeLock().lock(); - try { - long dif = otherDelayTimeStamp - delayTimeStamp; - if (dif > 0) { - return; - } - delayTimeStamp = otherDelayTimeStamp; - } finally { - lockOfDelayTimeStamp.writeLock().unlock(); - } - } - - public long getDelayTimeStamp() { - lockOfDelayTimeStamp.readLock().lock(); - try { - return delayTimeStamp; - } finally { - lockOfDelayTimeStamp.readLock().unlock(); - } - } - - public long clearTimeStamp() { - lockOfDelayTimeStamp.writeLock().lock(); - try { - long old = this.delayTimeStamp; - delayTimeStamp = Long.MAX_VALUE; - return old; - } finally { - lockOfDelayTimeStamp.writeLock().unlock(); - } - } - - @Override - public String toString() { - return "InspectorNode{" + - "delayTimeStamp=" + delayTimeStamp + - ", lockOfDelayTimeStamp=" + lockOfDelayTimeStamp + - '}'; - } - } - - /** - * 将被轮询的WrapperFinishInspection集合。 - */ - private final Map inspectionMap = new ConcurrentSkipListMap<>(); - - /** - * 请求轮询。 - */ - private void tryPolling() { - // 开始轮询 - SINGLETON_POLLING_POOL.submit(() -> { - // 用来判断在轮询过程中是否有新增的inspector的值 - int expectCount; - // 如果此值变化过,则在结束时让自己在此值后的时间再启动轮询 - while (!inspectionMap.isEmpty()) { - // expectCount是本线程用来记录本次循环开始时inspectionMap的个数。 - // 每当移出一个inspector时,该值-1。 - expectCount = inspectionMap.size(); - // 开始检查 - for (Map.Entry entry : inspectionMap.entrySet()) { - final WrapperEndingInspector inspector = entry.getKey(); - final InspectorNode inspectorNode = entry.getValue(); - // 直接抢锁,轮询期间禁止修改inspector - inspector.modifyPollingLock.writeLock().lock(); - try { - // 对一个inspector进行检查 - if (PollingCenter.this.checkInspectorIsEnd(inspector, inspectorNode)) { - // inspector中的wrapper调用结束了 - // 先要把wrapper给停了 - inspector.wrappers.forEach((wrapper, wrapperNode) -> { - WorkerWrapper.TimeOutProperties timeOut = wrapper.getTimeOut(); - if (timeOut != null) { - timeOut.checkTimeOut(true); - } - }); - // 修改此inspector和expectCount的状态 - if (inspector.endCDL.getCount() > 0) { - // 双重检查使endCDL原子性countDown。 - synchronized (inspector.endCDL) { - if (inspector.endCDL.getCount() > 0) { - inspectionMap.remove(inspector); - expectCount--; - inspector.endCDL.countDown(); - } - } - } - } - } finally { - inspector.modifyPollingLock.writeLock().unlock(); - } - } - /* - * 根据 expectCount == inspectionMap.size() 的值,在仅有本线程1个线程在轮询的情况下: - * 1. 若值为true,表示轮询过程中没有新的inspector被添加进set中。此时就可以break了。 - * . 之所以可以break,是因为这个inspection还没有调用结束,在其结束前还会来催促轮询的。 - * 2. 若值为false,表示有新的inspector在本线程轮询时,被加入到了set中,且没有被我们迭代到。此时还要重新轮询一次。 - */ - if (expectCount == inspectionMap.size()) { - break; - } - } - }); - } - - private boolean checkInspectorIsEnd(WrapperEndingInspector inspector, InspectorNode inspectorNode) { - // 判断一下inspector整组是否超时 - if (inspector.latestFinishTime < SystemClock.now()) { - inspector.haveNotTimeOut.set(false); - inspector.wrappers.forEach(((wrapper, wrapperNode) -> { - wrapper.failNow(); - wrapperNode.called.set(true); - })); - return true; - } - // 将延迟检查时间设为离现在最近的值。 - // 此处判断的是inspector所代表整次任务的超时时间 - inspectorNode.compareAndSetMinDelayTimeStamp(inspector.latestFinishTime); - // 判断inspector是否结束,并顺便记录、判断、修改wrapper的超时信息 - for (Map.Entry entry : inspector.wrappers.entrySet()) { - WorkerWrapper wrapper = entry.getKey(); - // 判断单个wrapper是否超时 - WorkerWrapper.TimeOutProperties timeOutProperties = wrapper.getTimeOut(); - if (timeOutProperties != null && timeOutProperties.isEnable()) { - // 将延迟检查时间设为离现在最近的值。 - // 此处判断的是wrapper的超时时间 - if (timeOutProperties.checkTimeOut(true)) { - inspector.haveNotTimeOut.set(false); - } - // 未超时但是设置了超时检查的话,记录一下inspector延时轮询时间 - else { - inspectorNode.compareAndSetMinDelayTimeStamp( - (timeOutProperties.isStarted() ? timeOutProperties.getStartWorkingTime() : SystemClock.now()) - + timeOutProperties.getUnit().toMillis(timeOutProperties.getTime()) - ); - } - } - // 判断wrapper是否执行完毕 - WrapperNode node = entry.getValue(); - if (wrapper.getState() == WorkerWrapper.INIT - // 上值如果为false,表示该Wrapper要么还没来得及执行,要么判断不需要执行但是还未被移出 - || !node.called.get() - // 上值如果为false,表示该Wrapper正在工作或是刚刚结束/失败,还未将所有下游Wrapper调用一遍。 - ) { - return false; - } - } - return true; - } - - { - final String executorName = "asyncTool-pollingDelayCaller"; - ScheduledThreadPoolExecutor delayPollingExecutor = new ScheduledThreadPoolExecutor( - 1, - new ThreadFactory() { - private final AtomicLong threadCount = new AtomicLong(0); - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, executorName + "-thread-" + threadCount.getAndIncrement()); - t.setDaemon(true); - // 线程优先级不高 - t.setPriority(1); - return t; - } - - @Override - public String toString() { - return executorName + "-threadFactory"; - } - } - ) { - @Override - public String toString() { - return executorName + "{PollingCenter.this=" + PollingCenter.this + "}"; - } - }; - // 每毫秒判断一次:map.value的每个延迟轮询队列的头号元素是否抵达当前时间,如果到了,则清除并调用轮询 - delayPollingExecutor.scheduleAtFixedRate(() -> inspectionMap.values().stream() - .min(Comparator.comparingLong(InspectorNode::getDelayTimeStamp)) - .ifPresent(node -> { - long delayTimeStamp = node.getDelayTimeStamp(); - if (Long.MAX_VALUE != delayTimeStamp && SystemClock.now() > delayTimeStamp) { - tryPolling(); - } - }), 1, 1, TimeUnit.MILLISECONDS); - } - - // ========== static ========== - - private final static PollingCenter instance = new PollingCenter(); - - public static PollingCenter getInstance() { - return instance; - } - - /** - * 单线程的轮询线程池 - */ - private static final ThreadPoolExecutor SINGLETON_POLLING_POOL; - - static { - SINGLETON_POLLING_POOL = new ThreadPoolExecutor( - 0, - // 轮询线程数必须为1 - 1, - 15L, - TimeUnit.SECONDS, - // 必须保存至少一个轮询请求,以便在本线程轮询结束时,获取到已轮询过的线程提交的轮询请求 - new ArrayBlockingQueue<>(1), - new ThreadFactory() { - private final AtomicLong threadCount = new AtomicLong(0); - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r, "asyncTool-pollingCenterPool-thread-" + threadCount.getAndIncrement()); - t.setDaemon(true); - // 线程优先级不高 - t.setPriority(3); - return t; - } - - @Override - public String toString() { - return "asyncTool-pollingCenterPool-threadFactory"; - } - }, - // 多的就丢了,反正都是催这一个线程去轮询 - new ThreadPoolExecutor.DiscardPolicy() - ) { - @Override - public String toString() { - return "asyncTool-pollingCenterPool"; - } - }; - } - } - -} - diff --git a/src/main/java/com/jd/platform/async/wrapper/skipstrategy/SkipStrategy.java b/src/main/java/com/jd/platform/async/wrapper/skipstrategy/SkipStrategy.java deleted file mode 100644 index d98aaa2..0000000 --- a/src/main/java/com/jd/platform/async/wrapper/skipstrategy/SkipStrategy.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.jd.platform.async.wrapper.skipstrategy; - -import com.jd.platform.async.wrapper.WorkerWrapper; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * @author create by TcSnZh on 2021/5/6-下午3:02 - */ -@FunctionalInterface -public interface SkipStrategy { - /** - * 跳过策略函数。返回true将会使WorkerWrapper跳过执行。 - * - * @param nextWrappers 下游WrapperSet - * @param thisWrapper 本WorkerWrapper - * @param fromWrapper 呼叫本Wrapper的上游Wrapper - * @return 返回true将会使WorkerWrapper跳过执行。 - */ - boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper); - - /** - * 不跳过 - */ - SkipStrategy NOT_SKIP = new SkipStrategy() { - @Override - public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - return false; - } - - @Override - public String toString() { - return "NOT_SKIP"; - } - }; - - SkipStrategy CHECK_ONE_LEVEL = new SkipStrategy() { - private final SkipStrategy searchNextOneLevel = searchNextWrappers(SearchNextWrappers.SearchType.DFS, 1); - - @Override - public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - return searchNextOneLevel.shouldSkip(nextWrappers, thisWrapper, fromWrapper); - } - - @Override - public String toString() { - return "CHECK_ONE_LEVEL"; - } - }; - - default SearchNextWrappers searchNextWrappers(SearchNextWrappers.SearchType searchType, int searchLevel) { - return new SearchNextWrappers(searchType, searchLevel); - } - - /** - * 检查之后的Wrapper是否不在INIT状态 - */ - class SearchNextWrappers implements SkipStrategy { - /** - * 搜索策略 - */ - enum SearchType { - DFS, BFS; - } - - private final SearchType searchType; - - /** - * 搜索深度 - */ - private final int searchLevel; - - public SearchNextWrappers(SearchType searchType, int searchLevel) { - this.searchType = Objects.requireNonNull(searchType); - this.searchLevel = searchLevel; - } - - @Override - public boolean shouldSkip(Set> nextWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper) { - Set> nextSet; - if ((nextSet = nextWrappers) == null || nextSet.isEmpty()) { - return false; - } - switch (searchType) { - case DFS: - return nextSet.stream().allMatch(next -> - next.getState() != WorkerWrapper.INIT || dfsSearchShouldSkip(next, 1)); - case BFS: - LinkedList queue = nextSet.stream().map(ww -> new BfsNode(ww, 0)).collect(Collectors.toCollection(LinkedList::new)); - HashSet> existed = new HashSet<>(nextSet); - while (!queue.isEmpty()) { - BfsNode node = queue.poll(); - if (node.atLevel > searchLevel) { - continue; - } - if (node.wrapper.getState() != WorkerWrapper.INIT) { - return true; - } - if (node.atLevel < searchLevel) { - // 如果不是深度的最大值,则往队列里添加 - node.wrapper.getNextWrappers().forEach(nextWrapper -> { - if (existed.contains(nextWrapper)) { - return; - } - queue.offer(new BfsNode(nextWrapper, node.atLevel + 1)); - existed.add(nextWrapper); - }); - } - } - return false; - default: - throw new IllegalStateException("searchType type illegal : " + searchType); - } - } - - private boolean dfsSearchShouldSkip(WorkerWrapper currentWrapper, int currentLevel) { - if (currentLevel + 1 > searchLevel || currentWrapper == null) { - return false; - } - for (WorkerWrapper nextWrapper : currentWrapper.getNextWrappers()) { - if (nextWrapper != null && - (nextWrapper.getState() != WorkerWrapper.INIT - || dfsSearchShouldSkip(nextWrapper, currentLevel + 1))) { - return true; - } - } - return false; - } - - static class BfsNode { - final WorkerWrapper wrapper; - final int atLevel; - - public BfsNode(WorkerWrapper wrapper, int atLevel) { - this.wrapper = wrapper; - this.atLevel = atLevel; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BfsNode bfsNode = (BfsNode) o; - return Objects.equals(wrapper, bfsNode.wrapper); - } - - @Override - public int hashCode() { - return wrapper.hashCode(); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SearchNextWrappers that = (SearchNextWrappers) o; - return searchLevel == that.searchLevel && searchType == that.searchType; - } - - @Override - public int hashCode() { - return searchLevel ^ searchType.ordinal(); - } - - @Override - public String toString() { - return "CheckNextWrapper{" + - "searchType=" + searchType + - ", searchLevel=" + searchLevel + - '}'; - } - } -} -- Gitee