From 13be645314915f66de9b840151146346c7ca3594 Mon Sep 17 00:00:00 2001 From: TcSnZh <1293969878@qq.com> Date: Thu, 13 May 2021 00:24:44 +0800 Subject: [PATCH] =?UTF-8?q?v1.5.2=20=E9=87=8D=E6=9E=84=E4=BA=86=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E4=B8=8D=E8=89=AF=E4=BB=A3=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86QuickStart=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QuickStart.md | 1216 ++++++++++++++--- .../jd/platform/async/callback/IWorker.java | 2 +- .../async/exception/SkippedException.java | 1 + .../com/jd/platform/async/executor/Async.java | 27 +- .../async/executor/PollingCenter.java | 87 ++ .../async/util/collection/WheelIterator.java | 23 + .../async/util/timer/AbstractWheelTimer.java | 17 + .../async/util/timer/HashedWheelTimer.java | 685 ++++++++++ .../jd/platform/async/util/timer/Timeout.java | 36 + .../jd/platform/async/util/timer/Timer.java | 41 + .../platform/async/util/timer/TimerTask.java | 10 + .../jd/platform/async/worker/WorkResult.java | 27 +- .../async/wrapper/StableWorkerWrapper.java | 16 +- .../wrapper/StableWorkerWrapperBuilder.java | 68 +- .../platform/async/wrapper/WorkerWrapper.java | 881 ++++++------ .../async/wrapper/WorkerWrapperBuilder.java | 27 +- .../async/wrapper/WorkerWrapperGroup.java | 155 +++ .../async/wrapper/WrapperEndingInspector.java | 486 ------- .../wrapper/skipstrategy/SkipStrategy.java | 183 --- .../depend}/DependMustStrategyMapper.java | 2 +- .../depend}/DependWrapperActionStrategy.java | 2 +- .../depend}/DependWrapperStrategyMapper.java | 2 +- .../depend}/DependenceAction.java | 13 +- .../depend}/DependenceStrategy.java | 28 +- .../wrapper/strategy/skip/SkipStrategy.java | 53 + src/test/java/beforev14/depend/DeWorker.java | 2 +- src/test/java/beforev14/depend/DeWorker1.java | 2 +- src/test/java/beforev14/depend/DeWorker2.java | 2 +- .../java/beforev14/depend/LambdaTest.java | 6 +- .../java/beforev14/dependnew/DeWorker.java | 2 +- .../java/beforev14/dependnew/DeWorker1.java | 2 +- .../java/beforev14/dependnew/DeWorker2.java | 2 +- .../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 +- .../java/beforev14/seq/SeqTimeoutWorker.java | 2 +- src/test/java/beforev14/seq/SeqWorker.java | 2 +- src/test/java/beforev14/seq/SeqWorker1.java | 2 +- src/test/java/beforev14/seq/SeqWorker2.java | 2 +- src/test/java/v15/cases/Case1.java | 62 + src/test/java/v15/cases/Case2.java | 53 + src/test/java/v15/cases/Case3.java | 61 + src/test/java/v15/cases/Case4.java | 69 + src/test/java/v15/cases/Case5.java | 75 + src/test/java/v15/cases/Case6.java | 61 + src/test/java/v15/cases/Case7.java | 61 + src/test/java/v15/cases/Case8.java | 73 + .../v15/{dependnew => wrappertest}/Test.java | 14 +- 51 files changed, 3302 insertions(+), 1353 deletions(-) create mode 100644 src/main/java/com/jd/platform/async/executor/PollingCenter.java create mode 100644 src/main/java/com/jd/platform/async/util/collection/WheelIterator.java create mode 100644 src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java create mode 100644 src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java create mode 100644 src/main/java/com/jd/platform/async/util/timer/Timeout.java create mode 100644 src/main/java/com/jd/platform/async/util/timer/Timer.java create mode 100644 src/main/java/com/jd/platform/async/util/timer/TimerTask.java create mode 100644 src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.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 rename src/main/java/com/jd/platform/async/wrapper/{actionstrategy => strategy/depend}/DependMustStrategyMapper.java (98%) rename src/main/java/com/jd/platform/async/wrapper/{actionstrategy => strategy/depend}/DependWrapperActionStrategy.java (98%) rename src/main/java/com/jd/platform/async/wrapper/{actionstrategy => strategy/depend}/DependWrapperStrategyMapper.java (97%) rename src/main/java/com/jd/platform/async/wrapper/{actionstrategy => strategy/depend}/DependenceAction.java (80%) rename src/main/java/com/jd/platform/async/wrapper/{actionstrategy => strategy/depend}/DependenceStrategy.java (93%) create mode 100644 src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java create mode 100644 src/test/java/v15/cases/Case1.java create mode 100644 src/test/java/v15/cases/Case2.java create mode 100644 src/test/java/v15/cases/Case3.java create mode 100644 src/test/java/v15/cases/Case4.java create mode 100644 src/test/java/v15/cases/Case5.java create mode 100644 src/test/java/v15/cases/Case6.java create mode 100644 src/test/java/v15/cases/Case7.java create mode 100644 src/test/java/v15/cases/Case8.java rename src/test/java/v15/{dependnew => wrappertest}/Test.java (96%) diff --git a/QuickStart.md b/QuickStart.md index f191d47..125a6d9 100644 --- a/QuickStart.md +++ b/QuickStart.md @@ -1,39 +1,43 @@ 如果只是需要用这个框架,请往下看即可。如果需要深入了解这个框架是如何一步一步实现的,从接到需求,到每一步的思考,每个类为什么这么设计,为什么有这些方法,也就是如何从0到1开发出这个框架,作者在[csdn开了专栏](https://blog.csdn.net/tianyaleixiaowu/category_9637010.html)专门讲中间件如何从0开发,包括并不限于这个小框架。京东内部同事可在cf上搜索erp也能看到。 +# 安装教程 + +代码不多,直接拷贝包过去即可。 + 京东同事通过引用如下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 - -``` - +# 使用说明 +### 基本组件 -#### 基本组件 worker: 一个最小的任务执行单元。通常是一个网络调用,或一段耗时操作。 T,V两个泛型,分别是入参和出参类型。 @@ -42,263 +46,1147 @@ 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; + } } ``` 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无法做到的。 + +### 简单示例 + +1. 3个任务并行 + +![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140256_8c015621_303698.png "屏幕截图.png") + +```java +class Test { + 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.beginWork(100, a, b, c); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + wrapper(id=C) is working + */ + } +} ``` -如 -![输入图片说明](https://images.gitee.com/uploads/images/2019/1225/132251_b7cfac23_303698.png "屏幕截图.png") - - 0执行完,同时1和2, 1\2都完成后3。3会等待2完成 - -此时,你可以定义一个 **worker** +2. 1个执行完毕后,开启另外两个,另外两个执行完毕后,开始第4个 + +![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140405_93800bc7_303698.png "屏幕截图.png") + +```java +class Test { + 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.beginWork(100, a); + } catch (ExecutionException | 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 Test { + 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); - } catch (InterruptedException e) { + Async.beginWork(100, a); + } catch (ExecutionException | 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.beginWork(1000, a, d); + } catch (ExecutionException | 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 + */ } +} +``` - @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()); +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 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.beginWork(20,wrapper100,wrapper200); + System.out.println(add.getWorkResult()); + // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} + } } +``` + +5. 其他的详见test包下的测试类,支持各种形式的组合、编排。 + +### 使用自定义线程池 +`Async`工具类有多个方法可以使用自定义线程池 + +```java +public static boolean beginWork(long timeout, + ExecutorService executorService, + Collection> workerWrappers); + +public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper); ``` -通过这一个类看一下,action里就是你的耗时操作,begin就是任务开始执行时的回调,result就是worker执行完毕后的回调。当你组合了多个执行单元时,每一步的执行,都在掌控之内。失败了,还会有自定义的默认值。这是CompleteableFuture无法做到的。 +另外,如果没有指定线程池,默认会使用`COMMON_POOL`,您可以调用这些方法获得/关闭此线程池: -#### 安装教程 +> 此线程池将会在第一次使用时懒加载。 -代码不多,直接拷贝包过去即可。 +```java +/** + * 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。 + */ +public static ThreadPoolExecutor getCommonPool(); -#### 使用说明 +/** + * @param now 是否立即关闭 + * @throws IllegalStateException 如果尚未调用过{@link #getCommonPool()},即没有使用过“使用默认线程池”的方法,该方法会抛出空指针异常。 + */ +public static synchronized void shutDownCommonPool(boolean now); +``` + +以下是一个使用自定义线程池的简单代码示例: + +```java +Async.beginWork(1000, Executors.newFixedThreadPool(2),a); +``` -1. 3个任务并行 -![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140256_8c015621_303698.png "屏幕截图.png") +## WorkerWrapper基本属性 + +### 执行流程 + +WorkerWrapper会在这些情况被运行: + +* 在`Async.beginWork`中传入wrapper +* 上游wrapper完成后被调用 + +开始运行时,执行逻辑如下图所示: + +![wrapper执行流程](https://gitee.com/tcsn_site/picture/raw/master/wrapper-process.png) + +> processOn流程图文件放在同仓库。 + +### 其他属性 + + + +## 设置WorkerWrapper属性 + +### 设置依赖策略 + +#### 快速上手 + +`WorkerWrapperBuilder`提供了这些方法来设置依赖策略: + +```java +public interface WorkerWrapperBuilder { + // 略 + + // 各种depends方法都是简便设置依赖的。 + default WorkerWrapperBuilder depends(/* 略 */); + + // 切换到SetDepend模式 + SetDepend setDepend(); + + interface SetDepend { + // 其中各种方法都是用来设置策略的,具体点开源码看注释就行。设置完end()回到Builder模式。 + } + + // 略 +} ``` - ParWorker w = new ParWorker(); - ParWorker1 w1 = new ParWorker1(); - ParWorker2 w2 = new ParWorker2(); - WorkerWrapper workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .build(); +如果没有具体设置策略的话,默认使用`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; + }); + } - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") + 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 workerWrapper = new WorkerWrapper.Builder() - .worker(w) - .callback(w) - .param("0") + 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.beginWork(1000, pool, a); + } 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 + */ + } +} +``` - long now = SystemClock.now(); - System.out.println("begin-" + now); +##### 策略器组件 - Async.beginWork(1500, workerWrapper, workerWrapper1, workerWrapper2); -// Async.beginWork(800, workerWrapper, workerWrapper1, workerWrapper2); -// Async.beginWork(1000, workerWrapper, workerWrapper1, workerWrapper2); +wrapper每次被上游wrapper所调用时,若是还没有结束且不应跳过,都会去根据自己的上游wrapper的状态,来决定自己这次该做什么。 - System.out.println("end-" + SystemClock.now()); - System.err.println("cost-" + (SystemClock.now() - now)); - System.out.println(Async.getThreadCount()); +`DependenceStrategy`这个函数式接口就是用来判断“这次该做什么的”。 - System.out.println(workerWrapper.getWorkResult()); - Async.shutDown(); - +```java +package com.jd.platform.async.wrapper.dependaction; + +//... + +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)}`的责任链设计模式,该返回值的意义就是调用责任链上下一个策略。 | + +##### 策略器组件默认实现 + +* `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`在判断时,并不是只使用一个策略进行判断的,而是在`WorkerWrapper.WrapperStrategy`进行了最多三层的判断: + +```java +public abstract class WorkerWrapper { + + // 略 + + 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; + + // 略 + } +} +``` + +正如注释所言,三个策略器将依次调用`DependenceStrategy.judgeAction(Set,WorkerWrapper,WorkerWrapper)`方法进行判断,每次判断会返回`DependenceAction.WithProperty`类型。 + +前两个策略器的返回值,即`DependenceAction.WithProperty`类型,若调用`getDependenceAction()`方法返回的枚举值不为`JUDGE_BY_AFTER`时,整个三层责任链将返回此返回值;若为`JUDGE_BY_AFTER`,则交给下个策略器进行判断。该方法具体由以下方法实现: +```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; + } + }; + } + + // 略 +} ``` - 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(); - WorkerWrapper workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .next(workerWrapper3) - .build(); - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") - .next(workerWrapper3) +#### 自定义依赖策略 + +##### 自定义全局策略 + +以下是一个自定义依赖策略的示例。 + +效果是,在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.beginWork(1000, pool, a); + } 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){ + /*...*/} + + // 略 + } + + // 略 +} +``` - WorkerWrapper workerWrapper = new WorkerWrapper.Builder() - .worker(w) - .callback(w) - .param("0") - .next(workerWrapper1, workerWrapper2) +以下是一个不推荐使用的示例: + +```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; + }); + } + + @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.beginWork(1000, pool, start); + } 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设置“上克下”策略 + +我们面临着这种问题: +> 一般来说,都是下游wrapper去根据上游wrapper的状态进行策略判断,然后给出响应。 - long now = SystemClock.now(); - System.out.println("begin-" + now); +那么,能不能让上游wrapper根据自己的状态独自决定下游wrapper响应呢? - Async.beginWork(3100, workerWrapper); -// Async.beginWork(2100, workerWrapper); +因此,三层策略器的`DependWrapperStrategyMapper`便是用于设置此功能的。 - System.out.println("end-" + SystemClock.now()); - System.err.println("cost-" + (SystemClock.now() - now)); +```java +// 示例版本v1.5 - System.out.println(Async.getThreadCount()); - Async.shutDown(); +/** + * 对不同的{@link WorkerWrapper}调用者实行个性化依赖响应策略。 + *

+ * 使用{@link DependWrapperStrategyMapper}本实现类对{@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); + + /** + * 设置对应策略 + * + * @param targetWrapper 要设置策略的WorkerWrapper + * @param strategy 要设置的策略 + * @return 返回this,链式调用。 + */ + public DependWrapperStrategyMapper putMapping(WorkerWrapper targetWrapper, DependWrapperActionStrategy strategy) {/* 略 */} + + /** + * 判断方法。 + *

+ * 如果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 // 仅判断该属性 + ) {/* 略 */} + + // 略 +} ``` -如果觉得这样不符合左右的顺序,也可以用这种方式: +其`mapper`属性中,每个`WorkerWrapper`对应了一个`DependWrapperActionStrategy`,这个接口便是用于让上游wrapper决定下游响应的: +```java +@FunctionalInterface +public interface DependWrapperActionStrategy { + /** + * 仅使用一个参数的判断方法 + * + * @param fromWrapper 调用本Wrapper的上游Wrapper + * @return 返回 {@link DependenceAction.WithProperty} + */ + DependenceAction.WithProperty judge(WorkerWrapper fromWrapper); + + // 常量略 +} ``` - 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 workerWrapper2 = new WorkerWrapper.Builder() - .worker(w2) - .callback(w2) - .param("2") - .depend(workerWrapper) - .next(workerWrapper3) - .build(); +* `DependWrapperActionStrategy.SUCCESS_CONTINUE` + * 成功时,交给下一个策略器判断。未运行时,休息。失败时,失败。 +* `DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE` + * 成功时,开始工作。未运行时,交给下一个策略器判断。失败时,失败。 + +###### 简单使用与示例 + +我们在`SetDepend setDepend();`模式时,可以使用如下方法进行设置 - WorkerWrapper workerWrapper1 = new WorkerWrapper.Builder() - .worker(w1) - .callback(w1) - .param("1") - .depend(workerWrapper) - .next(workerWrapper3) +```java +/** + * 对单个Wrapper设置特殊策略。 + * + * @param wrapper 需要设置特殊策略的Wrapper。 + * @param strategy 特殊策略。 + */ +SetDepend specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper wrapper); + +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救他")) + ) .build(); + WorkerWrapper a = builder("A") + .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()); + /* 输出: + wrapper(id=A) is working + wrapper(id=B) is working + WorkResult{result=null, resultState=SUCCESS, ex=null} + WorkResult{result=null, resultState=SUCCESS, ex=null} + */ + } +} ``` -3. 复杂点的 +### 设置跳过策略 -![输入图片说明](https://images.gitee.com/uploads/images/2019/1226/140445_8d52e4d6_303698.png "屏幕截图.png") +当wrapper发现下游wrapper居然已经被执行了,那是不是可以跳过自身呢? -在测试类里能找到,下图是执行结果。看时间戳,就知道执行的顺序。每个执行单元都是睡1秒。 + of course,当wrapper被跳过时,其`getWorkResult`返回的值通常是: -![输入图片说明](https://images.gitee.com/uploads/images/2019/1225/133828_0c76624c_303698.png "屏幕截图.png") +```json +{ + result: null, + resultState: ResultState.EXCEPTION, + ex: com.jd.platform.async.exception.SkippedException +} +``` -4. 依赖别的worker执行结果作为入参 +下面是跳过策略的设置接口: + +```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 略 + +} +``` -可以从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") +有以下几个默认值: -5. 其他的详见test包下的测试类,支持各种形式的组合、编排。 +* `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.beginWork(1000, a); + /* 输出: + 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.beginWork(15, a); + /* 输出: + 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) + 以下异常信息省略 + */ + } +} +``` + + + +### 设置是否允许被打断线程 + +可通过该选项去设置允许线程被打断: + +```java +public interface WorkerWrapperBuilder { + // 略 + + /** + * 是否允许被试图中断线程 + * + * @param allow 是则true + */ + WorkerWrapperBuilder allowInterrupt(boolean allow); + + // 略 +} +``` + +开启之后,在以下情况,会试图打断正处于WORKING状态的工作线程。 + +* 总任务超时,但本wrapper在WORKING。 +* 单wrapper超时,但本wrapper在WORKING。 +* wrapper应当被跳过,但本wrapper在WORKING。 + +* 调用`WorkerWrapper#failNow()`方法,且wrapper在WORKING状态。 +### diff --git a/src/main/java/com/jd/platform/async/callback/IWorker.java b/src/main/java/com/jd/platform/async/callback/IWorker.java index ffe000a..d25995c 100755 --- a/src/main/java/com/jd/platform/async/callback/IWorker.java +++ b/src/main/java/com/jd/platform/async/callback/IWorker.java @@ -17,7 +17,7 @@ public interface IWorker { * @param object object * @param allWrappers 任务包装 */ - V action(T object, Map allWrappers); + V action(T object, Map> allWrappers); /** * 超时、异常时,返回的默认值 diff --git a/src/main/java/com/jd/platform/async/exception/SkippedException.java b/src/main/java/com/jd/platform/async/exception/SkippedException.java index 807e8ec..8d820e0 100644 --- a/src/main/java/com/jd/platform/async/exception/SkippedException.java +++ b/src/main/java/com/jd/platform/async/exception/SkippedException.java @@ -2,6 +2,7 @@ package com.jd.platform.async.exception; /** * 如果任务在执行之前,自己后面的任务已经执行完或正在被执行,则抛该exception + * * @author wuweifeng wrote on 2020-02-18 * @version 1.0 */ diff --git a/src/main/java/com/jd/platform/async/executor/Async.java b/src/main/java/com/jd/platform/async/executor/Async.java index 7dcf855..a38ab22 100644 --- a/src/main/java/com/jd/platform/async/executor/Async.java +++ b/src/main/java/com/jd/platform/async/executor/Async.java @@ -5,7 +5,7 @@ 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.wrapper.WorkerWrapper; -import com.jd.platform.async.wrapper.WrapperEndingInspector; +import com.jd.platform.async.wrapper.WorkerWrapperGroup; import java.util.*; import java.util.concurrent.*; @@ -29,32 +29,29 @@ public class Async { */ public static boolean beginWork(long timeout, ExecutorService executorService, - Collection> workerWrappers) + Collection> workerWrappers) throws InterruptedException { if (workerWrappers == null || workerWrappers.size() == 0) { return false; } //保存上次执行的线程池变量(为了兼容以前的旧功能) 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); + WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout); + 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(); + return group.awaitFinish(); //处理超时的逻辑被移动到了WrapperEndingInspector中。 } /** * 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL */ + @SuppressWarnings("unchecked") public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException { if (workerWrapper == null || workerWrapper.length == 0) { @@ -138,6 +135,9 @@ public class Async { */ private static volatile ExecutorService lastExecutorService; + /** + * 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。 + */ public static ThreadPoolExecutor getCommonPool() { if (COMMON_POOL == null) { synchronized (Async.class) { @@ -181,7 +181,14 @@ public class Async { ",largestCount=" + COMMON_POOL.getLargestPoolSize(); } + /** + * @param now 是否立即关闭 + * @throws IllegalStateException 如果尚未调用过{@link #getCommonPool()},即没有使用过“使用默认线程池”的方法,该方法会抛出空指针异常。 + */ public static synchronized void shutDownCommonPool(boolean now) { + if (COMMON_POOL == null) { + throw new IllegalStateException("COMMON_POOL Not initialized yet"); + } if (!COMMON_POOL.isShutdown()) { if (now) { COMMON_POOL.shutdownNow(); diff --git a/src/main/java/com/jd/platform/async/executor/PollingCenter.java b/src/main/java/com/jd/platform/async/executor/PollingCenter.java new file mode 100644 index 0000000..66d4cab --- /dev/null +++ b/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.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.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 java.util.concurrent.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; + }, + 4, + 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/util/collection/WheelIterator.java b/src/main/java/com/jd/platform/async/util/collection/WheelIterator.java new file mode 100644 index 0000000..97ea47d --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/collection/WheelIterator.java @@ -0,0 +1,23 @@ +package com.jd.platform.async.util.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/src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java b/src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java new file mode 100644 index 0000000..cc2d711 --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/timer/AbstractWheelTimer.java @@ -0,0 +1,17 @@ +package com.jd.platform.async.util.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(); + + @Override + public void close() throws Exception { + stop(); + } +} diff --git a/src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java b/src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java new file mode 100644 index 0000000..864e9cd --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/timer/HashedWheelTimer.java @@ -0,0 +1,685 @@ +package com.jd.platform.async.util.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里抄来的,删去了一些功能。 + *

+ * + * 如果违反开源协议,请联系作者: zh0u.he@qq.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. + */ + 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 + */ + 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 + */ + 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, 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); + } + + /** + * 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. + * @param maxPendingTimeouts The maximum number of pending timeouts after which call to + * {@code newTimeout} will result in + * {@link java.util.concurrent.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) { + + 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 { + 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/src/main/java/com/jd/platform/async/util/timer/Timeout.java b/src/main/java/com/jd/platform/async/util/timer/Timeout.java new file mode 100644 index 0000000..1bbc9b1 --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/timer/Timeout.java @@ -0,0 +1,36 @@ +package com.jd.platform.async.util.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 + */ + boolean cancel(); +} diff --git a/src/main/java/com/jd/platform/async/util/timer/Timer.java b/src/main/java/com/jd/platform/async/util/timer/Timer.java new file mode 100644 index 0000000..3ab2052 --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/timer/Timer.java @@ -0,0 +1,41 @@ +package com.jd.platform.async.util.timer; + +import java.util.Set; +import java.util.concurrent.*; +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); + + 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/src/main/java/com/jd/platform/async/util/timer/TimerTask.java b/src/main/java/com/jd/platform/async/util/timer/TimerTask.java new file mode 100644 index 0000000..6ea3c76 --- /dev/null +++ b/src/main/java/com/jd/platform/async/util/timer/TimerTask.java @@ -0,0 +1,10 @@ +package com.jd.platform.async.util.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/src/main/java/com/jd/platform/async/worker/WorkResult.java b/src/main/java/com/jd/platform/async/worker/WorkResult.java index 95e5868..6d32659 100755 --- a/src/main/java/com/jd/platform/async/worker/WorkResult.java +++ b/src/main/java/com/jd/platform/async/worker/WorkResult.java @@ -1,5 +1,7 @@ package com.jd.platform.async.worker; +import java.util.concurrent.atomic.AtomicReference; + /** * 执行结果 */ @@ -7,12 +9,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 +26,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 +48,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/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapper.java b/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/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/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java index 5f98330..5fed611 100644 --- a/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java +++ b/src/main/java/com/jd/platform/async/wrapper/StableWorkerWrapperBuilder.java @@ -3,8 +3,11 @@ 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.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.DependWrapperActionStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependWrapperStrategyMapper; +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; @@ -93,6 +96,9 @@ 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<>()); @@ -329,8 +350,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) -> { + DependWrapperStrategyMapper dependWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper(); + if (dependWrapperStrategyMapper == null) { + next.getWrapperStrategy().setDependWrapperStrategyMapper( + dependWrapperStrategyMapper = new DependWrapperStrategyMapper()); + } + dependWrapperStrategyMapper.putMapping(wrapper, strategy); + }); } } // ========== 设置检查是否跳过策略 ========== @@ -344,18 +371,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; } diff --git a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java b/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java index fe074bd..32eef82 100755 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java +++ b/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java @@ -4,17 +4,23 @@ 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.PollingCenter; 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 com.jd.platform.async.wrapper.strategy.skip.SkipStrategy; +import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependWrapperStrategyMapper; +import com.jd.platform.async.wrapper.strategy.depend.DependenceAction; +import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import static com.jd.platform.async.wrapper.WorkerWrapper.State.*; /** * 对每个worker及callback进行包装,一对一 @@ -24,313 +30,407 @@ import java.util.concurrent.atomic.AtomicInteger; * @author wuweifeng wrote on 2019-11-19. */ public abstract class WorkerWrapper { + // ========== 固定属性 ========== + /** * 该wrapper的唯一标识 */ protected final String id; + protected final IWorker worker; + protected final ICallback callback; /** - * worker将来要处理的param + * 各种策略的封装类。 */ - protected T param; - protected IWorker worker; - protected ICallback callback; + private final WrapperStrategy wrapperStrategy = new WrapperStrategy(); /** - * 标记该事件是否已经被处理过了,譬如已经超时返回false了,后续rpc又收到返回值了,则不再二次回调 - * 经试验,volatile并不能保证"同一毫秒"内,多线程对该值的修改和拉取 - *

- * 1-finish, 2-error, 3-working + * 是否允许被打断 */ - protected final AtomicInteger state = new AtomicInteger(0); + protected final boolean allowInterrupt; /** - * 也是个钩子变量,用来存临时的结果 + * 是否启动超时检查 */ - protected volatile WorkResult workResult = WorkResult.defaultResult(); + final boolean enableTimeout; /** - * 该map存放所有wrapper的id和wrapper映射 - *

- * 需要线程安全。 + * 超时时间长度 */ - private Map> forParamUseWrappers; + final long timeoutLength; /** - * 各种策略的封装类。 - *

- * 其实是因为加功能太多导致这个对象大小超过了128Byte,所以强迫症的我不得不把几个字段丢到策略类里面去。 - * ps: 大小超过128Byte令我(TcSnZh)难受的一比,就像走在草坪的格子上,一步嫌小、两步扯蛋。 - * IDEA可以使用JOL Java Object Layout插件查看对象大小。 + * 超时时间单位 */ - private final WrapperStrategy wrapperStrategy = new WrapperStrategy(); + final TimeUnit timeoutUnit; + + // ========== 临时属性 ========== + /** - * 超时检查,该值允许为null。表示不设置。 + * worker将来要处理的param */ - private volatile TimeOutProperties timeOutProperties; - - // ***** state属性的常量值 ***** + 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); - 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) { + WorkerWrapper(String id, + IWorker worker, + ICallback callback, + boolean allowInterrupt, + boolean enableTimeout, + long timeoutLength, + TimeUnit timeoutUnit + ) { 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; + this.allowInterrupt = allowInterrupt; + this.enableTimeout = enableTimeout; + this.timeoutLength = timeoutLength; + this.timeoutUnit = timeoutUnit; + } // ========== public ========== /** - * 外部调用本线程运行此Wrapper的入口方法。 + * 外部调用本线程运行此wrapper的入口方法。 + * 该方法将会确定这组wrapper所属的group。 * - * @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。 - * @param remainTime 剩下的时间 - * @param forParamUseWrappers 用于保存经过的wrapper的信息的Map,key为id。 - * @param inspector wrapper调度检查器 + * @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。 + * @param remainTime 剩下的时间 + * @param group wrapper组 + * @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。 */ public void work(ExecutorService executorService, long remainTime, - Map> forParamUseWrappers, - WrapperEndingInspector inspector) { - work(executorService, null, remainTime, forParamUseWrappers, inspector); + WorkerWrapperGroup group) { + work(executorService, null, remainTime, group); } public String getId() { return id; } + /** + * 返回{@link #workResult}的值。 + * 若调用此方法时workResult还未设置,将会返回{@link WorkResult#defaultResult()}。 + */ public WorkResult getWorkResult() { - return workResult; + WorkResult res = workResult.get(); + return res == null ? WorkResult.defaultResult() : res; } public void setParam(T param) { this.param = param; } - public int getState() { - return state.get(); + public State getState() { + return State.of(state.get()); } /** - * 获取之后的下游Wrapper + * 获取下游Wrapper */ public abstract Set> getNextWrappers(); /** - * 使wrapper状态修改为超时失败。(但如果已经执行完成则不会修改) - *

- * 本方法不会试图执行超时判定逻辑。 - * 如果要执行超时逻辑判断,请用{@link TimeOutProperties#checkTimeOut(boolean)}并传入参数true。 + * 获取上游wrapper */ - public void failNow() { - int state = getState(); - if (state == INIT || state == WORKING) { - fastFail(state, null); - } - } - - public WrapperStrategy getWrapperStrategy() { - return wrapperStrategy; - } - - // ========== protected ========== + public abstract Set> getDependWrappers(); /** - * 快速失败 + * 获取本wrapper的超时情况。如有必要还会修改wrapper状态。 * - * @return 已经失败则返回false,如果刚才设置为失败了则返回true。 + * @param withEndIt 如果为true,在检查出已经超时的时候,会将其快速结束。 + * @param startTime 起始时间 + * @param totalTimeLength 总任务时长 + * @return 超时返回-1L,结束但未超时返回0L,尚未结束且未超时返回与deadline的差值 + *

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

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

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

*/ - 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); + public long checkTimeout(boolean withEndIt, long startTime, long totalTimeLength) { + do { + WorkResult _workResult = workResult.get(); + // 如果已经有结果了,就根据结果值判断 + if (_workResult != null) { + return _workResult.getResultState() == ResultState.TIMEOUT ? -1L : 0L; } - workResult.setResult(worker.defaultValue()); - } - callback.result(false, param, workResult); - return true; + // 如果还没有出结果 + // 判断是否超时 + 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); + } + return -1L; + } + // 正在运行,尚未超时 + else { + return Math.max(1, startTime + totalTimeLength - now); + } + } while (true); } - /** - * 判断{@link #state}状态是否是初始值。 - */ - protected boolean checkIsNullResult() { - return ResultState.DEFAULT == workResult.getResultState(); + public WrapperStrategy getWrapperStrategy() { + return wrapperStrategy; } - protected boolean compareAndSetState(int expect, int update) { - return this.state.compareAndSet(expect, update); - } + // ========== protected ========== /** * 工作的核心方法。 * * @param fromWrapper 代表这次work是由哪个上游wrapper发起的。如果是首个Wrapper则为null。 * @param remainTime 剩余时间。 + * @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。 */ protected void work(ExecutorService executorService, WorkerWrapper fromWrapper, long remainTime, - Map> forParamUseWrappers, - WrapperEndingInspector inspector) { - this.setForParamUseWrappers(forParamUseWrappers); - //将自己放到所有wrapper的集合里去 - forParamUseWrappers.put(id, this); + WorkerWrapperGroup group + ) { 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; - } + // ================================================ + // 以下是一些lambda。 + // 因为抽取成方法反而不好传参、污染类方法,所以就这么干了 + final Consumer __function__callbackResult = + success -> { + try { + callback.result(success, param, getWorkResult()); + } catch (Exception e) { + if (State.setState(state, states_of_skipOrAfterWork, ERROR, null)) { + fastFail(false, e); + } + } + }; + final Runnable __function__callbackResult_beginNext = + () -> { + __function__callbackResult.accept(false); + beginNext(executorService, now, remainTime, group); + }; + final BiConsumer __function__fastFail_callbackResult_beginNext = + (fastFail_isTimeout, fastFail_exception) -> { + fastFail(fastFail_isTimeout, fastFail_exception); + __function__callbackResult_beginNext.run(); + }; + final Runnable __function__doWork = + () -> { + if (State.setState(state, STARTED, WORKING)) { + try { + fire(group); + } catch (Exception e) { + if (State.setState(state, WORKING, ERROR)) { + __function__fastFail_callbackResult_beginNext.accept(false, e); + } + return; + } + } + if (State.setState(state, WORKING, AFTER_WORK)) { + __function__callbackResult.accept(true); + beginNext(executorService, now, remainTime, group); + } + }; + // ================================================ + // 开始执行 + try { + if (State.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); + } + return; + } + //如果自己已经执行过了。 + //可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了 + final AtomicReference oldStateRef = new AtomicReference<>(null); + if (!State.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 (State.setState(state, states_of_checkTimeoutAllowStates, ERROR, null)) { + __function__fastFail_callbackResult_beginNext.accept(false, e); + } + return; + } + } - // 判断是否要跳过自己,该方法可能会跳过正在工作的自己。 - final WrapperStrategy wrapperStrategy = getWrapperStrategy(); - if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) { - fastFail(INIT, new SkippedException()); - beginNext(executorService, now, remainTime, inspector); - return; - } + //如果fromWrapper为null,说明自己就是第一批要执行的 + if (fromWrapper == null) { + // 首当其冲,开始工作 + __function__doWork.run(); + return; + } - //如果没有任何依赖,说明自己就是第一批要执行的 - final Set> dependWrappers = getDependWrappers(); - if (dependWrappers == null || dependWrappers.size() == 0) { - fire(); - beginNext(executorService, now, remainTime, inspector); - return; + // 每个线程都需要判断是否要跳过自己,该方法可能会跳过正在工作的自己。 + final WrapperStrategy wrapperStrategy = getWrapperStrategy(); + if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) { + if (State.setState(state, STARTED, SKIP)) { + __function__fastFail_callbackResult_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 (State.setState(state, STARTED, ERROR)) { + // 根据FAST_FAIL.fastFailException()设置的属性值来设置fastFail方法的参数 + ResultState resultState = judge.getResultState(); + __function__fastFail_callbackResult_beginNext.accept( + resultState == ResultState.TIMEOUT, + judge.getFastFailException() + ); + } + return; + case START_WORK: + __function__doWork.run(); + return; + case JUDGE_BY_AFTER: + default: + throw new Error("策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + ",fromWrapper=" + fromWrapper); + } + } catch (Exception e) { + // wrapper本身抛出了不该有的异常 + State.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); } + } - 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); + /** + * 本工作线程执行自己的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 timeout 是否是因为超时而快速失败 + * @param e 设置异常信息到{@link WorkResult#getEx()} + */ + protected void fastFail(boolean timeout, Exception e) { + // 试图打断正在执行{@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(), + timeout ? ResultState.TIMEOUT : ResultState.EXCEPTION, + e + )); } /** * 进行下一个任务 + *

+ * 本方法不负责校验状态。请在调用前自行检验 */ - protected void beginNext(ExecutorService executorService, long now, long remainTime, WrapperEndingInspector inspector) { + 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) { - inspector.setWrapperEndWithTryPolling(this); + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); return; } // nextWrappers只有一个,就用本线程继续跑。 if (nextWrappers.size() == 1) { + WorkerWrapper next = null; try { - WorkerWrapper next = nextWrappers.stream().findFirst().get(); - inspector.addWrapper(next); - next.work(executorService, WorkerWrapper.this, nextRemainTIme, getForParamUseWrappers(), inspector); + next = nextWrappers.stream().findFirst().get(); + group.addWrapper(next); + State.setState(state, AFTER_WORK, SUCCESS); } finally { - inspector.setWrapperEndWithTryPolling(this); + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); + if (next != null) { + next.work(executorService, this, nextRemainTIme, group); + } } - 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; + else { try { - callback.begin(); - if (timeOutProperties != null) { - timeOutProperties.startWorking(); - } - //执行耗时操作 - resultValue = (V) worker.action(param, (Map) getForParamUseWrappers()); + group.addWrapper(nextWrappers); + nextWrappers.forEach(next -> executorService.submit(() -> + next.work(executorService, this, nextRemainTIme, group)) + ); + State.setState(state, AFTER_WORK, SUCCESS); } finally { - if (timeOutProperties != null) { - timeOutProperties.endWorking(); - } - } - //如果状态不是在working,说明别的地方已经修改了 - if (!compareAndSetState(WORKING, FINISH)) { - return; + PollingCenter.getInstance().checkGroup(group.new CheckFinishTask()); } - workResult.setResultState(ResultState.SUCCESS); - workResult.setResult(resultValue); - //回调成功 - callback.result(true, param, workResult); - } catch (Exception e) { - //避免重复回调 - if (!checkIsNullResult()) { - return; - } - fastFail(WORKING, e); } + } + // ========== private ========== + // ========== hashcode and equals ========== @Override @@ -347,7 +447,7 @@ public abstract class WorkerWrapper { return id.hashCode(); } - // ========== Builder ========== + // ========== builder ========== public static WorkerWrapperBuilder builder() { return new Builder<>(); @@ -365,111 +465,51 @@ public abstract class WorkerWrapper { } } - // ========== 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; - } + // ========== package access methods ========== 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) + final StringBuilder sb = new StringBuilder(400) .append("WorkerWrapper{id=").append(id) + .append(", state=").append(State.of(state.get())) .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) + .append(", allowInterrupt=").append(allowInterrupt) + .append(", enableTimeout=").append(enableTimeout) + .append(", timeoutLength=").append(timeoutLength) + .append(", timeoutUnit=").append(timeoutUnit) // 防止循环引用,这里只输出相关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) { + .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=["); - getNextWrappers().stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", ")); - if (getNextWrappers().size() > 0) { + 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("]") - .append(", wrapperStrategy=").append(getWrapperStrategy()) - .append(", timeOutProperties=").append(getTimeOut()) + .append("], doWorkingThread=").append(doWorkingThread.get()) + .append(", worker=").append(worker) + .append(", callback=").append(callback) + .append(", wrapperStrategy=").append(wrapperStrategy) .append('}'); return sb.toString(); } public static class WrapperStrategy implements DependenceStrategy, SkipStrategy { - // ========== 这三个属性用来判断是否要开始工作 ========== + // ========== 这三个策略器用于链式判断是否要开始工作 ========== // 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy @@ -486,7 +526,7 @@ public abstract class WorkerWrapper { */ private DependMustStrategyMapper dependMustStrategyMapper; /** - * 依赖响应全局策略。 + * 底层全局策略。 */ private DependenceStrategy dependenceStrategy; @@ -562,159 +602,184 @@ public abstract class WorkerWrapper { } } - 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(); - } - } - - // ========== 轮询线程调用 ========== - + /** + * state状态枚举工具类 + */ + public enum State { /** - * 检查超时。 - * 可以将boolean参数传入true以在超时的时候直接失败。 - * - * @param withStop 如果为false,不会发生什么,仅仅是单纯的判断是否超时。 - * 如果为true,则会去快速失败wrapper{@link #failNow()},有必要的话还会打断线程。 - * @return 如果 超时 或 执行时间超过限制 返回true;未超时返回false。 + * 初始化中,builder正在设置其数值 */ - 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; - } + BUILDING(-1), + /** + * 初始化完成,但是还未执行过。 + */ + INIT(0), + /** + * 执行过。 + * 即至少进行了一次各种判定,例如判断 是否跳过/是否启动工作 + */ + STARTED(1), + /** + * 工作状态 + */ + WORKING(2), + /** + * 工作完成后的收尾工作,例如调用下游wrapper + */ + AFTER_WORK(3), + /** + * wrapper成功执行结束 + */ + SUCCESS(4), + /** + * wrapper失败了 + */ + ERROR(5), + /** + * wrapper被跳过 + */ + SKIP(6); - // ========== package ========== + // public - boolean isEnable() { - return enable; + public boolean finished() { + return this == SUCCESS || this == ERROR || this == SKIP; } - long getTime() { - return time; - } + // package - TimeUnit getUnit() { - return unit; + State(int id) { + this.id = id; } - boolean isAllowInterrupt() { - return allowInterrupt; - } + final int id; - Object getLock() { - return lock; - } + // package-static - boolean isStarted() { - return started; - } + static final State[] states_of_notWorked = new State[]{INIT, STARTED}; - void setStarted(boolean started) { - this.started = started; - } + static final State[] states_of_skipOrAfterWork = new State[]{SKIP, AFTER_WORK}; - boolean isEnded() { - return ended; - } + static final State[] states_of_checkTimeoutAllowStates = new State[]{INIT, STARTED, WORKING}; - void setEnded(boolean ended) { - this.ended = ended; - } + static final State[] states_all = new State[]{BUILDING, INIT, STARTED, WORKING, AFTER_WORK, SUCCESS, ERROR, SKIP}; - long getStartWorkingTime() { - return startWorkingTime; + /** + * 自旋+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; + } + } } - void setStartWorkingTime(long startWorkingTime) { - this.startWorkingTime = startWorkingTime; + /** + * 自旋+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; } - long getEndWorkingTime() { - return endWorkingTime; + /** + * 自旋+CAS的判断是否在这些excepts范围内 + * + * @param excepts 范围。 + */ + 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; + } + } } - void setEndWorkingTime(long endWorkingTime) { - this.endWorkingTime = endWorkingTime; + /** + * CAS的判断是否是某个状态 + */ + static boolean isState(AtomicInteger state, State except) { + return state.compareAndSet(except.id, except.id); } - Thread getDoWorkingThread() { - return doWorkingThread; + static State of(int id) { + return id2state.get(id); } - void setDoWorkingThread(Thread doWorkingThread) { - this.doWorkingThread = doWorkingThread; + static final Map id2state; + + static { + HashMap map = new HashMap<>(); + for (State s : State.values()) { + map.put(s.id, s); + } + id2state = Collections.unmodifiableMap(map); } - // ========== 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 + - '}'; + /** + * 这是因未知错误而引发的异常 + */ + 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/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java index 4a519c0..7a1f909 100644 --- a/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperBuilder.java +++ b/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.DependWrapperActionStrategy; +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; @@ -257,13 +256,6 @@ public interface WorkerWrapperBuilder { */ SetTimeOut setTime(long time, TimeUnit unit); - /** - * 是否允许被试图中断线程 - * - * @param allow 是则true - */ - SetTimeOut allowInterrupt(boolean allow); - WorkerWrapperBuilder end(); } @@ -274,13 +266,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/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java b/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java new file mode 100644 index 0000000..528d646 --- /dev/null +++ b/src/main/java/com/jd/platform/async/wrapper/WorkerWrapperGroup.java @@ -0,0 +1,155 @@ +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; +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.AtomicLong; +import java.util.stream.Stream; + +/** + * @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); + + private final AtomicBoolean anyTimeout = new AtomicBoolean(false); + + 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; + } + + /** + * 同步等待这组wrapper执行完成 + * + * @return false代表有wrapper超时了。true代表全部wrapper没有超时。 + */ + public boolean awaitFinish() throws InterruptedException { + endCDL.await(); + return !anyTimeout.get(); + } + + public class CheckFinishTask implements TimerTask { + + @Override + public void run(Timeout timeout) throws Exception { + // 已经完成了 + if (endCDL.getCount() < 1) { + return; + } + AtomicBoolean hasTimeout = new AtomicBoolean(false); + // 记录正在运行中的wrapper里,最近的限时时间。 + 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 + // 处理超时 + .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; + } + do { + long getMinDaley = minDaley.get(); + if (getMinDaley <= time_diff || minDaley.compareAndSet(getMinDaley, time_diff)) { + return; + } + } while (true); + }) + // 判断是否结束,这里如果还有未结束的wrapper则会提前结束流。 + .allMatch(wrapper -> wrapper.getState().finished()); + long getMinDaley = minDaley.get(); + // 如果有正在运行的wrapper + if (!allFinish) { + // 如果有正在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 + + /** + * 将会调用{@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 WorkerWrapperGroup.CheckFinishTask)) { + return false; + } + return Objects.equals(WorkerWrapperGroup.this, ((CheckFinishTask) obj).getParent()); + } + + private WorkerWrapperGroup getParent() { + return WorkerWrapperGroup.this; + } + } +} 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 + - '}'; - } - } -} diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java similarity index 98% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java rename to src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependMustStrategyMapper.java index 8ad1019..ff1108a 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependMustStrategyMapper.java +++ b/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; diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java similarity index 98% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java rename to src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.java index 745168f..fa5cef2 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperActionStrategy.java +++ b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperActionStrategy.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; diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java similarity index 97% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java rename to src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.java index 5c2cd56..802e1c2 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependWrapperStrategyMapper.java +++ b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependWrapperStrategyMapper.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; diff --git a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceAction.java b/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 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/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/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java similarity index 93% rename from src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceStrategy.java rename to src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java index 56368ea..be00042 100644 --- a/src/main/java/com/jd/platform/async/wrapper/actionstrategy/DependenceStrategy.java +++ b/src/main/java/com/jd/platform/async/wrapper/strategy/depend/DependenceStrategy.java @@ -1,6 +1,5 @@ -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.worker.ResultState; import com.jd.platform.async.worker.WorkResult; import com.jd.platform.async.wrapper.WorkerWrapper; @@ -41,7 +40,7 @@ public interface DependenceStrategy { *

* @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty} */ - DependenceAction.WithProperty judgeAction(Set> dependWrappers, + DependenceAction.WithProperty judgeAction(Set> dependWrappers, WorkerWrapper thisWrapper, WorkerWrapper fromWrapper); @@ -55,7 +54,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 +80,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 +118,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; @@ -159,7 +158,7 @@ public interface DependenceStrategy { */ 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) { @@ -188,7 +187,7 @@ public interface DependenceStrategy { * @param theseWrapper 该方法唯一有效参数。 * @return 返回生成的 {@link DependenceAction.WithProperty) */ - static DependenceStrategy theseWrapperAllSuccess(Set> theseWrapper) { + static DependenceStrategy theseWrapperAllSuccess(Set> theseWrapper) { return new DependenceStrategy() { private final Set> theseWrappers; private final String toString; @@ -199,7 +198,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 +231,16 @@ 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开关 + */ + @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/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java b/src/main/java/com/jd/platform/async/wrapper/strategy/skip/SkipStrategy.java new file mode 100644 index 0000000..13bd5a1 --- /dev/null +++ b/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.*; + +/** + * @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/src/test/java/beforev14/depend/DeWorker.java index e9f2b82..725ab84 100755 --- a/src/test/java/beforev14/depend/DeWorker.java +++ b/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/src/test/java/beforev14/depend/DeWorker1.java index b958768..38bfbf6 100755 --- a/src/test/java/beforev14/depend/DeWorker1.java +++ b/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/src/test/java/beforev14/depend/DeWorker2.java index c2a48c4..3567643 100755 --- a/src/test/java/beforev14/depend/DeWorker2.java +++ b/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/src/test/java/beforev14/depend/LambdaTest.java index 93037a5..68a8386 100644 --- a/src/test/java/beforev14/depend/LambdaTest.java +++ b/src/test/java/beforev14/depend/LambdaTest.java @@ -13,7 +13,7 @@ import com.jd.platform.async.wrapper.WorkerWrapper; 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 +28,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 +44,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/dependnew/DeWorker.java b/src/test/java/beforev14/dependnew/DeWorker.java index 135b6b3..b5b0b01 100755 --- a/src/test/java/beforev14/dependnew/DeWorker.java +++ b/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/src/test/java/beforev14/dependnew/DeWorker1.java index ba02503..79418db 100755 --- a/src/test/java/beforev14/dependnew/DeWorker1.java +++ b/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/src/test/java/beforev14/dependnew/DeWorker2.java index 304df06..bba8e86 100755 --- a/src/test/java/beforev14/dependnew/DeWorker2.java +++ b/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/parallel/ParTimeoutWorker.java b/src/test/java/beforev14/parallel/ParTimeoutWorker.java index f0a2f3a..8e393ec 100755 --- a/src/test/java/beforev14/parallel/ParTimeoutWorker.java +++ b/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/src/test/java/beforev14/parallel/ParWorker.java index b28b7e6..58f8694 100755 --- a/src/test/java/beforev14/parallel/ParWorker.java +++ b/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/src/test/java/beforev14/parallel/ParWorker1.java index 414851c..87592bc 100755 --- a/src/test/java/beforev14/parallel/ParWorker1.java +++ b/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/src/test/java/beforev14/parallel/ParWorker2.java index 87cc0ac..a456c33 100755 --- a/src/test/java/beforev14/parallel/ParWorker2.java +++ b/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/src/test/java/beforev14/parallel/ParWorker3.java index 82b6299..148c6ab 100755 --- a/src/test/java/beforev14/parallel/ParWorker3.java +++ b/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/src/test/java/beforev14/parallel/ParWorker4.java index 7f9c267..9046ee2 100755 --- a/src/test/java/beforev14/parallel/ParWorker4.java +++ b/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/seq/SeqTimeoutWorker.java b/src/test/java/beforev14/seq/SeqTimeoutWorker.java index 80a5c7b..3297bcb 100755 --- a/src/test/java/beforev14/seq/SeqTimeoutWorker.java +++ b/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/src/test/java/beforev14/seq/SeqWorker.java index c2bc392..ebb451a 100755 --- a/src/test/java/beforev14/seq/SeqWorker.java +++ b/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/src/test/java/beforev14/seq/SeqWorker1.java index b3ded50..f1544d1 100755 --- a/src/test/java/beforev14/seq/SeqWorker1.java +++ b/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/src/test/java/beforev14/seq/SeqWorker2.java index 458db80..1002f68 100755 --- a/src/test/java/beforev14/seq/SeqWorker2.java +++ b/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/v15/cases/Case1.java b/src/test/java/v15/cases/Case1.java new file mode 100644 index 0000000..11075ba --- /dev/null +++ b/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.beginWork(1000, a, d); + } catch (ExecutionException | 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/src/test/java/v15/cases/Case2.java b/src/test/java/v15/cases/Case2.java new file mode 100644 index 0000000..bd2758d --- /dev/null +++ b/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.beginWork(20,wrapper100,wrapper200); + System.out.println(add.getWorkResult()); + // 输出WorkResult{result=300, resultState=SUCCESS, ex=null} + } +} diff --git a/src/test/java/v15/cases/Case3.java b/src/test/java/v15/cases/Case3.java new file mode 100644 index 0000000..d149e07 --- /dev/null +++ b/src/test/java/v15/cases/Case3.java @@ -0,0 +1,61 @@ +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.beginWork(1000, pool, a); + } 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 + */ + } +} diff --git a/src/test/java/v15/cases/Case4.java b/src/test/java/v15/cases/Case4.java new file mode 100644 index 0000000..657f7a7 --- /dev/null +++ b/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.beginWork(1000, pool, a); + } 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/src/test/java/v15/cases/Case5.java b/src/test/java/v15/cases/Case5.java new file mode 100644 index 0000000..7a96e6d --- /dev/null +++ b/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.beginWork(1000, pool, start); + } 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/src/test/java/v15/cases/Case6.java b/src/test/java/v15/cases/Case6.java new file mode 100644 index 0000000..d54d855 --- /dev/null +++ b/src/test/java/v15/cases/Case6.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.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设置“上克下”策略--简单使用与示例 + * + * @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 { + WorkerWrapper b = builder("B") + // 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。 + .depends((dependWrappers, thisWrapper, fromWrapper) -> + DependenceAction.FAST_FAIL + .fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他")) + ) + .build(); + WorkerWrapper a = builder("A") + .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()); + /* 输出: + 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/src/test/java/v15/cases/Case7.java b/src/test/java/v15/cases/Case7.java new file mode 100644 index 0000000..1be8b93 --- /dev/null +++ b/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.beginWork(1000, a); + /* 输出: + wrapper(id=A) is working + wrapper(id=E) is working + wrapper(id=B) is working + wrapper(id=D) is working + */ + } +} diff --git a/src/test/java/v15/cases/Case8.java b/src/test/java/v15/cases/Case8.java new file mode 100644 index 0000000..2972c9d --- /dev/null +++ b/src/test/java/v15/cases/Case8.java @@ -0,0 +1,73 @@ +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.beginWork(15, a); + /* 输出: + 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/src/test/java/v15/dependnew/Test.java b/src/test/java/v15/wrappertest/Test.java similarity index 96% rename from src/test/java/v15/dependnew/Test.java rename to src/test/java/v15/wrappertest/Test.java index d0b149b..696e3f6 100644 --- a/src/test/java/v15/dependnew/Test.java +++ b/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); -- Gitee