mirror of
https://gitee.com/jd-platform-opensource/asyncTool.git
synced 2026-03-22 04:27:15 +08:00
v1.5.1-SNAPSHOT 1.rebase 2.重构不良代码 3.更新QuickStart.md 4.分模块 5.使用任务句柄OnceWork 6.增加任务取消功能
This commit is contained in:
40
.gitignore
vendored
40
.gitignore
vendored
@@ -1,32 +1,32 @@
|
||||
HELP.md
|
||||
target/
|
||||
**/HELP.md
|
||||
**/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
**/.apt_generated
|
||||
**/.classpath
|
||||
**/.factorypath
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.springBeans
|
||||
**/.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
**/.idea
|
||||
**/*.iws
|
||||
**/*.iml
|
||||
**/*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
**/nbproject/private/
|
||||
**/nbbuild/
|
||||
**/dist/
|
||||
**/nbdist/
|
||||
**/.nb-gradle/
|
||||
**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
**/.vscode/
|
||||
|
||||
1552
QuickStart.md
1552
QuickStart.md
File diff suppressed because it is too large
Load Diff
13
README.md
13
README.md
@@ -89,8 +89,16 @@
|
||||
|
||||
在V1.3后,框架支持在worker的action的入参Map<String, WorkerWrapper>中获取任意一个执行单元的执行结果,当然,可以取其中的1个、多个执行结果作为自己的入参。Key就是在定义wrapper时通过id传进来的唯一id标识。详情demo可以查看test包下dependnew包案例。
|
||||
|
||||
## 并发场景可能存在的需求之——全组任务的超时
|
||||
一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。
|
||||
## 并发场景可能存在的需求之——任务的超时
|
||||
> 在v1.4中:
|
||||
>
|
||||
> 一组任务,虽然内部的各个执行单元的时间不可控,但是我可以控制全组的执行时间不超过某个值。通过设置timeOut,来控制全组的执行阈值。
|
||||
|
||||
在v1.5中:
|
||||
|
||||
每个wrapper可以设置自己的超时时间,同时也可以设置整组任务的超时时间。
|
||||
|
||||
且可以设置一旦超时则打断线程(默认不启用打断线程)
|
||||
|
||||
## 并发场景可能存在的需求之——高性能、低线程数
|
||||
该框架全程无锁,不依靠线程锁来保证顺序。
|
||||
@@ -114,4 +122,3 @@
|
||||
## 快速开始
|
||||
[点此开启实战](https://gitee.com/jd-platform-opensource/asyncTool/blob/master/QuickStart.md)
|
||||
|
||||
|
||||
|
||||
27
asyncTool-core/pom.xml
Normal file
27
asyncTool-core/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>asyncTool</artifactId>
|
||||
<groupId>com.jd.platform</groupId>
|
||||
<version>1.5.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>asyncTool-core</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jd.platform</groupId>
|
||||
<artifactId>asyncTool-openutil</artifactId>
|
||||
<version>1.5.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -15,14 +15,10 @@ public class DefaultCallback<T, V> implements ICallback<T, V> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认将打印存在的非{@link com.jd.platform.async.exception.SkippedException}的异常。
|
||||
* 默认情况啥回调都没有,而且将吞掉所有异常显示(只保存在{@link WorkResult}中)
|
||||
*/
|
||||
@Override
|
||||
public void result(boolean success, T param, WorkResult<V> workResult) {
|
||||
Exception ex = workResult.getEx();
|
||||
if (ex != null && !(ex instanceof SkippedException)) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
46
asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java
Executable file
46
asyncTool-core/src/main/java/com/jd/platform/async/callback/ICallback.java
Executable file
@@ -0,0 +1,46 @@
|
||||
package com.jd.platform.async.callback;
|
||||
|
||||
|
||||
import com.jd.platform.async.exception.SkippedException;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
|
||||
/**
|
||||
* 每个执行单元执行完毕后,会回调该接口</p>
|
||||
* 需要监听执行结果的,实现该接口即可
|
||||
*
|
||||
* @author wuweifeng wrote on 2019-11-19.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ICallback<T, V> {
|
||||
/**
|
||||
* 任务开始的监听
|
||||
*/
|
||||
default void begin() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 耗时操作执行完毕后,就给value注入值
|
||||
* <p/>
|
||||
* 只要Wrapper被调用后成功或失败/超时,该方法都会被执行。
|
||||
*/
|
||||
void result(boolean success, T param, WorkResult<V> workResult);
|
||||
|
||||
/**
|
||||
* 提供常量选项:打印异常信息,跳过时的异常{@link SkippedException}不会打印。
|
||||
*/
|
||||
ICallback PRINT_EXCEPTION_STACK_TRACE = new ICallback<Object, Object>() {
|
||||
@Override
|
||||
public void result(boolean success, Object param, WorkResult<Object> workResult) {
|
||||
Exception ex = workResult.getEx();
|
||||
if (ex != null && !(ex instanceof SkippedException)) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PRINT_EXCEPTION_STACK_TRACE";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.jd.platform.async.callback;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 每个最小执行单元需要实现该接口
|
||||
*
|
||||
@@ -17,10 +17,10 @@ public interface IWorker<T, V> {
|
||||
* @param object object
|
||||
* @param allWrappers 任务包装
|
||||
*/
|
||||
V action(T object, Map<String, WorkerWrapper> allWrappers);
|
||||
V action(T object, Map<String, WorkerWrapper<?,?>> allWrappers);
|
||||
|
||||
/**
|
||||
* 超时、异常时,返回的默认值
|
||||
* 超时、异常、跳过时,返回的默认值
|
||||
*
|
||||
* @return 默认值
|
||||
*/
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jd.platform.async.exception;
|
||||
|
||||
/**
|
||||
* 整组取消,设置该异常。
|
||||
*
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午6:12
|
||||
*/
|
||||
public class CancelSkippedException extends SkippedException {
|
||||
public CancelSkippedException() {
|
||||
}
|
||||
|
||||
public CancelSkippedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CancelSkippedException(String message, long skipAt) {
|
||||
super(message, skipAt);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,30 @@
|
||||
package com.jd.platform.async.exception;
|
||||
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
|
||||
/**
|
||||
* 如果任务在执行之前,自己后面的任务已经执行完或正在被执行,则抛该exception
|
||||
*
|
||||
* @author wuweifeng wrote on 2020-02-18
|
||||
* @version 1.0
|
||||
*/
|
||||
public class SkippedException extends RuntimeException {
|
||||
private final long skipAt;
|
||||
|
||||
public SkippedException() {
|
||||
super();
|
||||
this(null);
|
||||
}
|
||||
|
||||
public SkippedException(String message) {
|
||||
this(message, SystemClock.now());
|
||||
}
|
||||
|
||||
public SkippedException(String message, long skipAt) {
|
||||
super(message);
|
||||
this.skipAt = skipAt;
|
||||
}
|
||||
|
||||
public long getSkipAt() {
|
||||
return skipAt;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,15 @@ package com.jd.platform.async.executor;
|
||||
import com.jd.platform.async.callback.DefaultGroupCallback;
|
||||
import com.jd.platform.async.callback.IGroupCallback;
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
import com.jd.platform.async.worker.OnceWork;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WrapperEndingInspector;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -23,61 +26,90 @@ public class Async {
|
||||
// ========================= 任务执行核心代码 =========================
|
||||
|
||||
/**
|
||||
* 出发点
|
||||
*
|
||||
* @return 只要执行未超时,就返回true。
|
||||
* {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。
|
||||
* 使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。
|
||||
*/
|
||||
public static boolean beginWork(long timeout,
|
||||
ExecutorService executorService,
|
||||
Collection<? extends WorkerWrapper<?,?>> workerWrappers)
|
||||
throws InterruptedException {
|
||||
if (workerWrappers == null || workerWrappers.size() == 0) {
|
||||
return false;
|
||||
public static OnceWork work(long timeout,
|
||||
Collection<? extends WorkerWrapper<?, ?>> workerWrappers) {
|
||||
return work(timeout, getCommonPool(), workerWrappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。
|
||||
* 可变参式传入。使用uuid作为工作id。使用{@link #getCommonPool()}作为线程池。
|
||||
*/
|
||||
public static OnceWork work(long timeout,
|
||||
WorkerWrapper<?, ?>... workerWrappers) {
|
||||
return work(timeout, getCommonPool(), workerWrappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。
|
||||
* 可变参式传入。使用uuid作为工作id。
|
||||
*/
|
||||
public static OnceWork work(long timeout,
|
||||
ExecutorService executorService,
|
||||
WorkerWrapper<?, ?>... workerWrappers) {
|
||||
return work(timeout, executorService, Arrays.asList(
|
||||
Objects.requireNonNull(workerWrappers, "workerWrappers array is null")));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #work(long, ExecutorService, Collection, String)}方法的简易封装。
|
||||
* 省略工作id,使用uuid。
|
||||
*/
|
||||
public static OnceWork work(long timeout,
|
||||
ExecutorService executorService,
|
||||
Collection<? extends WorkerWrapper<?, ?>> workerWrappers) {
|
||||
return work(timeout, executorService, workerWrappers, UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心方法。
|
||||
* 该方法不是同步阻塞执行的。如果想要同步阻塞执行,则调用返回值的{@link OnceWork#awaitFinish()}即可。
|
||||
*
|
||||
* @param timeout 全组超时时间
|
||||
* @param executorService 执行线程池
|
||||
* @param workerWrappers 任务容器集合
|
||||
* @param workId 本次工作id
|
||||
* @return 返回 {@link OnceWork}封装对象。
|
||||
*/
|
||||
public static OnceWork work(long timeout,
|
||||
ExecutorService executorService,
|
||||
Collection<? extends WorkerWrapper<?, ?>> workerWrappers,
|
||||
String workId) {
|
||||
if (workerWrappers == null || workerWrappers.isEmpty()) {
|
||||
return OnceWork.emptyWork(workId);
|
||||
}
|
||||
//保存上次执行的线程池变量(为了兼容以前的旧功能)
|
||||
Async.lastExecutorService = Objects.requireNonNull(executorService, "ExecutorService is null ! ");
|
||||
//定义一个map,存放所有的wrapper,key为wrapper的唯一id,value是该wrapper,可以从value中获取wrapper的result
|
||||
final ConcurrentMap<String, WorkerWrapper<?,?>> forParamUseWrappers =
|
||||
new ConcurrentHashMap<>(Math.max(workerWrappers.size() * 3, 8));
|
||||
final WrapperEndingInspector inspector = new WrapperEndingInspector(SystemClock.now() + timeout);
|
||||
inspector.addWrapper(workerWrappers);
|
||||
Async.lastExecutorService.set(Objects.requireNonNull(executorService, "ExecutorService is null ! "));
|
||||
final WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout);
|
||||
final OnceWork.Impl onceWork = new OnceWork.Impl(group, workId);
|
||||
group.addWrapper(workerWrappers);
|
||||
workerWrappers.forEach(wrapper -> {
|
||||
if (wrapper == null) {
|
||||
return;
|
||||
}
|
||||
executorService.submit(() -> wrapper.work(executorService, timeout, forParamUseWrappers, inspector));
|
||||
executorService.submit(() -> wrapper.work(executorService, timeout, group));
|
||||
});
|
||||
inspector.registerToPollingCenter();
|
||||
return inspector.await();
|
||||
//处理超时的逻辑被移动到了WrapperEndingInspector中。
|
||||
return onceWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL
|
||||
* @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。
|
||||
*/
|
||||
public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper)
|
||||
throws ExecutionException, InterruptedException {
|
||||
if (workerWrapper == null || workerWrapper.length == 0) {
|
||||
return false;
|
||||
}
|
||||
Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet());
|
||||
return beginWork(timeout, executorService, workerWrappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步阻塞,直到所有都完成,或失败
|
||||
*/
|
||||
public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException {
|
||||
return beginWork(timeout, getCommonPool(), workerWrapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public static void beginWorkAsync(long timeout, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) {
|
||||
beginWorkAsync(timeout, getCommonPool(), groupCallback, workerWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步执行,直到所有都完成,或失败后,发起回调
|
||||
*
|
||||
* @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。
|
||||
*/
|
||||
@Deprecated
|
||||
public static void beginWorkAsync(long timeout, ExecutorService executorService, IGroupCallback groupCallback, WorkerWrapper... workerWrapper) {
|
||||
if (groupCallback == null) {
|
||||
groupCallback = new DefaultGroupCallback();
|
||||
@@ -128,7 +160,7 @@ public class Async {
|
||||
* 该线程池将会给线程取名为asyncTool-commonPool-thread-0(数字不重复)。
|
||||
* </p>
|
||||
*/
|
||||
private static ThreadPoolExecutor COMMON_POOL;
|
||||
private static volatile ThreadPoolExecutor COMMON_POOL;
|
||||
|
||||
/**
|
||||
* 在以前(及现在)的版本中:
|
||||
@@ -136,8 +168,11 @@ public class Async {
|
||||
* <p/>
|
||||
* 注意,这里是个static,也就是只能有一个线程池。用户自定义线程池时,也只能定义一个
|
||||
*/
|
||||
private static volatile ExecutorService lastExecutorService;
|
||||
private static final AtomicReference<ExecutorService> lastExecutorService = new AtomicReference<>(null);
|
||||
|
||||
/**
|
||||
* 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。
|
||||
*/
|
||||
public static ThreadPoolExecutor getCommonPool() {
|
||||
if (COMMON_POOL == null) {
|
||||
synchronized (Async.class) {
|
||||
@@ -151,9 +186,11 @@ public class Async {
|
||||
new ThreadFactory() {
|
||||
private final AtomicLong threadCount = new AtomicLong(0);
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "asyncTool-commonPool-thread-" + threadCount.getAndIncrement());
|
||||
Thread t = new Thread(r,
|
||||
"asyncTool-commonPool-thread-" + threadCount.getAndIncrement());
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
@@ -181,7 +218,15 @@ public class Async {
|
||||
",largestCount=" + COMMON_POOL.getLargestPoolSize();
|
||||
}
|
||||
|
||||
public static synchronized void shutDownCommonPool(boolean now) {
|
||||
/**
|
||||
* @param now 是否立即关闭
|
||||
* @return 如果尚未调用过{@link #getCommonPool()},即没有初始化默认线程池,返回false。否则返回true。
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static synchronized boolean shutDownCommonPool(boolean now) {
|
||||
if (COMMON_POOL == null) {
|
||||
return false;
|
||||
}
|
||||
if (!COMMON_POOL.isShutdown()) {
|
||||
if (now) {
|
||||
COMMON_POOL.shutdownNow();
|
||||
@@ -189,6 +234,52 @@ public class Async {
|
||||
COMMON_POOL.shutdown();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ========================= deprecated =========================
|
||||
|
||||
/**
|
||||
* 同步执行一次任务。
|
||||
*
|
||||
* @return 只要执行未超时,就返回true。
|
||||
* @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean beginWork(long timeout,
|
||||
ExecutorService executorService,
|
||||
Collection<? extends WorkerWrapper<?, ?>> workerWrappers)
|
||||
throws InterruptedException {
|
||||
final OnceWork work = work(timeout, executorService, workerWrappers);
|
||||
work.awaitFinish();
|
||||
return work.hasTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步执行一次任务。
|
||||
* 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL
|
||||
*
|
||||
* @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper)
|
||||
throws ExecutionException, InterruptedException {
|
||||
if (workerWrapper == null || workerWrapper.length == 0) {
|
||||
return false;
|
||||
}
|
||||
Set workerWrappers = Arrays.stream(workerWrapper).collect(Collectors.toSet());
|
||||
//noinspection unchecked
|
||||
return beginWork(timeout, executorService, workerWrappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步阻塞,直到所有都完成,或失败
|
||||
*
|
||||
* @deprecated 已经被 {@link #work(long, ExecutorService, Collection, String)}方法取代。
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean beginWork(long timeout, WorkerWrapper... workerWrapper) throws ExecutionException, InterruptedException {
|
||||
return beginWork(timeout, getCommonPool(), workerWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,8 +293,9 @@ public class Async {
|
||||
*/
|
||||
@Deprecated
|
||||
public static void shutDown() {
|
||||
if (lastExecutorService != COMMON_POOL) {
|
||||
shutDown(lastExecutorService);
|
||||
final ExecutorService last = lastExecutorService.get();
|
||||
if (last != COMMON_POOL) {
|
||||
shutDown(last);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +306,7 @@ public class Async {
|
||||
* @deprecated 没啥用的方法,要关闭线程池还不如直接调用线程池的关闭方法,避免歧义。
|
||||
*/
|
||||
@Deprecated
|
||||
public static void shutDown(ExecutorService executorService) {
|
||||
public static void shutDown(@Nullable ExecutorService executorService) {
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jd.platform.async.executor;
|
||||
|
||||
import com.jd.platform.async.openutil.timer.HashedWheelTimer;
|
||||
import com.jd.platform.async.openutil.timer.Timeout;
|
||||
import com.jd.platform.async.openutil.timer.Timer;
|
||||
import com.jd.platform.async.openutil.timer.TimerTask;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 检查{@link WorkerWrapperGroup}是否调用完成的轮询中心。
|
||||
* 内部使用时间轮进行轮询。
|
||||
* <p>
|
||||
* ===========================================================================================
|
||||
* <p>
|
||||
* 在v1.4及以前的版本,存在如下问题:
|
||||
* >
|
||||
* 在使用线程数量较少的线程池进行beginWork时,调用WorkerWrapper#beginNext方法时,
|
||||
* 会因为本线程等待下游Wrapper执行完成而存在线程耗尽bug。线程池会死翘翘的僵住、动弹不得。
|
||||
* >
|
||||
* 例如仅有2个线程的线程池,执行以下任务:
|
||||
* {@code
|
||||
* <p>
|
||||
* 这是旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况,在test/v15.wrappertest中示例testThreadPolling_V14Bug说明了这个bug
|
||||
* 线程数:2
|
||||
* A(5ms)--B1(10ms) ---|--> C1(5ms)
|
||||
* . \ | (B1、B2任一完成可执行C1、C2)
|
||||
* . ---> B2(20ms) --|--> C2(5ms)
|
||||
* <p>
|
||||
* }
|
||||
* 线程1执行了A,然后在{@link CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。
|
||||
* 线程2执行了B1或B2中的一个,也在allOf方法等待C1、C2完成。
|
||||
* 结果没有线程执行C和B2了,导致超时而死,并且这个线程池线程有可能被耗尽。
|
||||
* >
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午9:22
|
||||
*/
|
||||
public class PollingCenter {
|
||||
|
||||
// ========== singleton instance ==========
|
||||
|
||||
private static final PollingCenter instance = new PollingCenter();
|
||||
|
||||
public static PollingCenter getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ========== fields and methods ==========
|
||||
|
||||
public void checkGroup(WorkerWrapperGroup.CheckFinishTask task) {
|
||||
checkGroup(task, 0);
|
||||
}
|
||||
|
||||
public void checkGroup(WorkerWrapperGroup.CheckFinishTask task, long daley) {
|
||||
timer.newTimeout(task, daley, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private final Timer timer = new Timer() {
|
||||
private final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(
|
||||
r -> {
|
||||
Thread thread = new Thread(r, "asyncTool-pollingThread");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
},
|
||||
1,
|
||||
TimeUnit.MILLISECONDS,
|
||||
1024);
|
||||
|
||||
@Override
|
||||
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
|
||||
return hashedWheelTimer.newTimeout(task, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Timeout> stop() {
|
||||
return hashedWheelTimer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PollingCenter.timer";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.jd.platform.async.executor.timer;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.jd.platform.async.worker;
|
||||
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 一次工作结果的总接口。
|
||||
*
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午3:22
|
||||
*/
|
||||
public interface OnceWork {
|
||||
/**
|
||||
* 返回唯一的workId
|
||||
*/
|
||||
String workId();
|
||||
|
||||
/**
|
||||
* 判断是否结束。因超时而结束也算结束。
|
||||
*/
|
||||
boolean isFinish();
|
||||
|
||||
/**
|
||||
* 同步等待到结束。
|
||||
*/
|
||||
void awaitFinish() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* 判断是否超时
|
||||
*
|
||||
* @return 如果尚未结束或已结束但未超时,返回false。已结束且已经超时返回true。
|
||||
*/
|
||||
boolean hasTimeout();
|
||||
|
||||
/**
|
||||
* 判断是否全部wrapper都处于 执行成功 或 跳过。
|
||||
*
|
||||
* @return 如果已经结束,所有wrapper都成功或跳过返回true,否则返回false。如果尚未结束,返回false。
|
||||
*/
|
||||
default boolean allSuccess() {
|
||||
if (!isFinish()) {
|
||||
return false;
|
||||
}
|
||||
return getWrappers().values().stream().allMatch(wrapper -> {
|
||||
final ResultState state = wrapper.getWorkResult().getResultState();
|
||||
return state == ResultState.SUCCESS || state == ResultState.DEFAULT;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部参与到工作中的wrapper。
|
||||
*/
|
||||
Map<String, WorkerWrapper<?, ?>> getWrappers();
|
||||
|
||||
/**
|
||||
* 获取{@link WorkResult#getResultState()}为{@link ResultState#SUCCESS}的wrapper。
|
||||
*/
|
||||
default Map<String, WorkerWrapper<?, ?>> getSuccessWrappers() {
|
||||
return getWrappersOfState(ResultState.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态于这些state中的wrapper。
|
||||
*
|
||||
* @param ofState 状态列表
|
||||
* @return 返回Map
|
||||
*/
|
||||
default Map<String, WorkerWrapper<?, ?>> getWrappersOfState(ResultState... ofState) {
|
||||
final HashSet<ResultState> states = new HashSet<>(Arrays.asList(ofState));
|
||||
return getWrappers().entrySet().stream()
|
||||
.filter(entry -> states.contains(entry.getValue().getWorkResult().getResultState()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启动时间
|
||||
*/
|
||||
long getStartTime();
|
||||
|
||||
/**
|
||||
* 获取结束时间
|
||||
*
|
||||
* @return 如果超时,返回超时的时刻。如果尚未结束,则抛出异常。
|
||||
* @throws IllegalStateException 尚未结束,抛出异常。
|
||||
*/
|
||||
long getFinishTime();
|
||||
|
||||
/**
|
||||
* @return 已经取消完成
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* @return 是否正在取消中
|
||||
*/
|
||||
boolean isWaitingCancel();
|
||||
|
||||
/**
|
||||
* 请求异步取消。
|
||||
*/
|
||||
void pleaseCancel();
|
||||
|
||||
/**
|
||||
* 同步等待取消完成。
|
||||
*/
|
||||
default void pleaseCancelAndAwaitFinish() throws InterruptedException {
|
||||
if (!isCancelled() && !isWaitingCancel()) {
|
||||
pleaseCancel();
|
||||
}
|
||||
awaitFinish();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回 {@link AsFuture}封装对象。
|
||||
*/
|
||||
default AsFuture asFuture() {
|
||||
return new AsFuture(this, limitTime -> limitTime / 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sleepCheckInterval 为防止线程爆炸,在{@link Future#get(long, TimeUnit)}方法时使用隔一段时间检查一次。
|
||||
* 该Function的参数为总超时毫秒值,返回值为检查时间间隔。
|
||||
* @return 返回 {@link AsFuture}封装对象。
|
||||
*/
|
||||
default AsFuture asFuture(Function<Long, Long> sleepCheckInterval) {
|
||||
return new AsFuture(this, sleepCheckInterval);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
/**
|
||||
* 空任务
|
||||
*/
|
||||
static OnceWork emptyWork(String workId) {
|
||||
return new EmptyWork(workId);
|
||||
}
|
||||
|
||||
// class
|
||||
|
||||
class AsFuture implements Future<Map<String, WorkerWrapper<?, ?>>> {
|
||||
private final OnceWork onceWork;
|
||||
private final Function<Long, Long> sleepCheckInterval;
|
||||
|
||||
private AsFuture(OnceWork onceWork, Function<Long, Long> sleepCheckInterval) {
|
||||
this.onceWork = onceWork;
|
||||
this.sleepCheckInterval = sleepCheckInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步等待取消
|
||||
*
|
||||
* @param ignore__mayInterruptIfRunning 该参数将被无视。因为暂未实现“修改允许打断属性”功能。 // todo 等待实现
|
||||
*/
|
||||
@Override
|
||||
public boolean cancel(boolean ignore__mayInterruptIfRunning) {
|
||||
try {
|
||||
onceWork.pleaseCancelAndAwaitFinish();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return onceWork.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return onceWork.isFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, WorkerWrapper<?, ?>> get() throws InterruptedException, ExecutionException {
|
||||
if (!onceWork.isFinish()) {
|
||||
onceWork.awaitFinish();
|
||||
}
|
||||
return onceWork.getWrappers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 避免线程爆炸,该方法不予单独开线程,而是单线程{@link Thread#sleep(long)}每睡一段时间检查一次。
|
||||
*/
|
||||
@Override
|
||||
public Map<String, WorkerWrapper<?, ?>> get(long timeout,
|
||||
@SuppressWarnings("NullableProblems") TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
final long millis = Objects.requireNonNull(unit).toMillis(timeout);
|
||||
final long interval = Math.max(1, Math.min(millis, sleepCheckInterval.apply(millis)));
|
||||
for (int i = 0; interval * i < millis; i++) {
|
||||
if (onceWork.isFinish()) {
|
||||
return onceWork.getWrappers();
|
||||
}
|
||||
Thread.sleep(interval);
|
||||
}
|
||||
throw new TimeoutException(
|
||||
"onceWork.asFuture.get(long,TimeUnit) out of time limit(" +
|
||||
timeout + "," + unit + ") , this is " + this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(asFuture from " + this + ")";
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractOnceWork implements OnceWork {
|
||||
protected final String workId;
|
||||
|
||||
public AbstractOnceWork(String workId) {
|
||||
this.workId = workId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String workId() {
|
||||
return workId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof OnceWork)) {
|
||||
return false;
|
||||
}
|
||||
OnceWork _o = (OnceWork) o;
|
||||
return Objects.equals(_o.workId(), this.workId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return workId().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final boolean finish;
|
||||
final StringBuilder sb = new StringBuilder(48)
|
||||
.append(this.getClass().getSimpleName())
|
||||
.append("{isFinish=").append(finish = isFinish())
|
||||
.append(", hasTimeout=").append(hasTimeout())
|
||||
.append(", allSuccess=").append(allSuccess())
|
||||
.append(", getStartTime=").append(getStartTime())
|
||||
.append(", isCancelled=").append(isCancelled())
|
||||
.append(", isWaitingCancel=").append(isWaitingCancel());
|
||||
if (finish) {
|
||||
sb.append(", getFinishTime=").append(getFinishTime());
|
||||
}
|
||||
return sb
|
||||
.append(", wrappers::getId=").append(getWrappers().keySet())
|
||||
.append('}').toString();
|
||||
}
|
||||
}
|
||||
|
||||
class Impl extends AbstractOnceWork {
|
||||
protected final WorkerWrapperGroup group;
|
||||
|
||||
public Impl(WorkerWrapperGroup group, String workId) {
|
||||
super(workId);
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinish() {
|
||||
return group.getEndCDL().getCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awaitFinish() throws InterruptedException {
|
||||
group.getEndCDL().await();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTimeout() {
|
||||
return group.getAnyTimeout().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, WorkerWrapper<?, ?>> getWrappers() {
|
||||
return group.getForParamUseWrappers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return group.getGroupStartTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFinishTime() {
|
||||
if (isFinish()) {
|
||||
throw new IllegalStateException("work not finish.");
|
||||
}
|
||||
return group.getFinishTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return group.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWaitingCancel() {
|
||||
return group.isWaitingCancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pleaseCancel() {
|
||||
group.pleaseCancel();
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyWork extends AbstractOnceWork {
|
||||
private final long initTime = SystemClock.now();
|
||||
|
||||
public EmptyWork(String workId) {
|
||||
super(workId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinish() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awaitFinish() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTimeout() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, WorkerWrapper<?, ?>> getWrappers() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return initTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFinishTime() {
|
||||
return initTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWaitingCancel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pleaseCancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(it's empty work)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ public class WorkResult<V> {
|
||||
/**
|
||||
* 执行的结果
|
||||
*/
|
||||
private V result;
|
||||
private final V result;
|
||||
/**
|
||||
* 结果状态
|
||||
*/
|
||||
private ResultState resultState;
|
||||
private Exception ex;
|
||||
private final ResultState resultState;
|
||||
private final Exception ex;
|
||||
|
||||
public WorkResult(V result, ResultState resultState) {
|
||||
this(result, resultState, null);
|
||||
@@ -24,10 +24,15 @@ public class WorkResult<V> {
|
||||
this.ex = ex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回不可修改的DEFAULT单例。
|
||||
*/
|
||||
public static <V> WorkResult<V> defaultResult() {
|
||||
return new WorkResult<>(null, ResultState.DEFAULT);
|
||||
return (WorkResult<V>) DEFAULT;
|
||||
}
|
||||
|
||||
private static final WorkResult<?> DEFAULT = new WorkResult<>(null, ResultState.DEFAULT);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WorkResult{" +
|
||||
@@ -41,23 +46,11 @@ public class WorkResult<V> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.openutil.collection.DirectedGraph;
|
||||
import com.jd.platform.async.openutil.collection.Graph;
|
||||
import com.jd.platform.async.wrapper.strategy.WrapperStrategy;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 快速构造{@link WorkerWrapper},少废话!
|
||||
* <p>
|
||||
* 直接设置属性,不用麻烦Builder设置来设置去,
|
||||
* 请 注 意:构造方法不会检查参数合法性,请程序员自己保证参数合法。
|
||||
* <p>
|
||||
* 将关系存储于有向图中{@link DirectedGraph}以节省每个wrapper都要保存节点数据的开销。
|
||||
* </p>
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/13-上午11:54
|
||||
*/
|
||||
public class QuickBuildWorkerWrapper<T, V> extends WorkerWrapper<T, V> {
|
||||
private final DirectedGraph<WorkerWrapper<?, ?>, Object> graph;
|
||||
|
||||
private volatile Set<WorkerWrapper<?, ?>> nextWrappersCache;
|
||||
private volatile Set<WorkerWrapper<?, ?>> dependWrappersCache;
|
||||
|
||||
/**
|
||||
* 构造函数,传入所有属性
|
||||
*
|
||||
* @param id {@link WorkerWrapper#id}
|
||||
* @param param {@link WorkerWrapper#param}
|
||||
* @param worker {@link WorkerWrapper#worker}
|
||||
* @param callback {@link WorkerWrapper#callback}
|
||||
* @param allowInterrupt {@link WorkerWrapper#allowInterrupt}
|
||||
* @param enableTimeout {@link WorkerWrapper#enableTimeout}
|
||||
* @param timeoutLength {@link WorkerWrapper#timeoutLength}
|
||||
* @param timeoutUnit {@link WorkerWrapper#timeoutLength}
|
||||
* @param wrapperStrategy {@link WorkerWrapper#timeoutUnit}
|
||||
* @param wrapperGraph 将节点信息保存在图中,而不是如{@link StableWorkerWrapper}在每个wrapper中都保存节点信息。
|
||||
* <p>
|
||||
* {@link WorkerWrapper#getDependWrappers()}与{@link WorkerWrapper#getNextWrappers()}方法
|
||||
* 将从本图中读取依赖顺序。除此之外,本类不会对本图进行任何修改操作。
|
||||
* 因此,传入的此图应当保证读取时的线程安全。
|
||||
* </p>
|
||||
*/
|
||||
public QuickBuildWorkerWrapper(String id,
|
||||
T param,
|
||||
IWorker<T, V> worker,
|
||||
ICallback<T, V> callback,
|
||||
boolean allowInterrupt,
|
||||
boolean enableTimeout,
|
||||
long timeoutLength,
|
||||
TimeUnit timeoutUnit,
|
||||
WrapperStrategy wrapperStrategy,
|
||||
DirectedGraph<WorkerWrapper<?, ?>, Object> wrapperGraph) {
|
||||
super(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, wrapperStrategy);
|
||||
graph = wrapperGraph;
|
||||
super.param = param;
|
||||
State.setState(state, State.BUILDING, State.INIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<WorkerWrapper<?, ?>> getNextWrappers() {
|
||||
if (nextWrappersCache == null) {
|
||||
synchronized (this) {
|
||||
if (nextWrappersCache == null) {
|
||||
nextWrappersCache = graph.getRelationFrom(this).stream()
|
||||
.map(Graph.Entry::getTo).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextWrappersCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<WorkerWrapper<?, ?>> getDependWrappers() {
|
||||
if (dependWrappersCache == null) {
|
||||
synchronized (this) {
|
||||
if (dependWrappersCache == null) {
|
||||
dependWrappersCache = graph.getRelationTo(this).stream()
|
||||
.map(Graph.Entry::getFrom).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
return dependWrappersCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setNextWrappers(Set<WorkerWrapper<?, ?>> nextWrappers) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDependWrappers(Set<WorkerWrapper<?, ?>> dependWrappers) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -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<T, V> extends WorkerWrapper<T, V> {
|
||||
StableWorkerWrapper(String id, IWorker<T, V> worker, T param, ICallback<T, V> callback) {
|
||||
super(id, worker, param, callback);
|
||||
public class StableWorkerWrapper<T, V> extends WorkerWrapper<T, V> {
|
||||
public StableWorkerWrapper(String id,
|
||||
IWorker<T, V> worker,
|
||||
ICallback<T, V> callback,
|
||||
boolean allowInterrupt,
|
||||
boolean enableTimeout,
|
||||
long timeoutLength,
|
||||
TimeUnit timeoutUnit) {
|
||||
super(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +52,6 @@ class StableWorkerWrapper<T, V> extends WorkerWrapper<T, V> {
|
||||
return nextWrappers;
|
||||
}
|
||||
|
||||
|
||||
// ========== package impl ==========
|
||||
|
||||
@Override
|
||||
@@ -54,7 +60,7 @@ class StableWorkerWrapper<T, V> extends WorkerWrapper<T, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<WorkerWrapper<?, ?>> getDependWrappers() {
|
||||
public Set<WorkerWrapper<?, ?>> getDependWrappers() {
|
||||
return dependWrappers;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.exception.SkippedException;
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.*;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -59,15 +63,15 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
/**
|
||||
* 存储自己需要特殊对待的dependWrapper集合
|
||||
*/
|
||||
private Map<WorkerWrapper<?, ?>, DependWrapperActionStrategy> dependWrapperActionStrategyMap;
|
||||
private Map<WorkerWrapper<?, ?>, DependOnUpWrapperStrategy> dependWrapperActionStrategyMap;
|
||||
/**
|
||||
* 存储需要特殊对待自己的nextWrapper集合。
|
||||
*/
|
||||
private Map<WorkerWrapper<?, ?>, DependWrapperActionStrategy> selfIsSpecialMap;
|
||||
private Map<WorkerWrapper<?, ?>, DependOnUpWrapperStrategy> selfIsSpecialMap;
|
||||
/**
|
||||
* 一个保存以must=true方式传入的WorkerWrapper的集合。
|
||||
* <p/>
|
||||
* 该Set将会加入到{@link WorkerWrapper.WrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中
|
||||
* 该Set将会加入到{@link WorkerWrapper.StableWrapperStrategy#getDependMustStrategyMapper().mustDependSet}之中
|
||||
*/
|
||||
private Set<WorkerWrapper<?, ?>> mustDependSet;
|
||||
/**
|
||||
@@ -93,6 +97,9 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
private boolean enableTimeOut = false;
|
||||
private long time = -1;
|
||||
private TimeUnit unit = null;
|
||||
/**
|
||||
* 是否允许被打断
|
||||
*/
|
||||
private boolean allowInterrupt = false;
|
||||
|
||||
/**
|
||||
@@ -166,7 +173,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetDependImpl specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper<?, ?> wrapper) {
|
||||
public SetDependImpl specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper<?, ?> wrapper) {
|
||||
if (strategy == null || wrapper == null) {
|
||||
return this;
|
||||
}
|
||||
@@ -226,7 +233,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetNextImpl specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerWrapper<?, ?> wrapper) {
|
||||
public SetNextImpl specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper<?, ?> wrapper) {
|
||||
if (strategy == null || wrapper == null) {
|
||||
return this;
|
||||
}
|
||||
@@ -259,33 +266,48 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
@Override
|
||||
public SetTimeOutImpl setTime(long time, TimeUnit unit) {
|
||||
if (time <= 0 || unit == null) {
|
||||
throw new IllegalStateException("Illegal argument : time=" + time + " must > 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<T, V> allowInterrupt(boolean allow) {
|
||||
allowInterrupt = allow;
|
||||
return returnThisBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkerWrapper<T, V> 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<T, V> wrapper = new StableWorkerWrapper<>(
|
||||
id == null ? UUID.randomUUID().toString() : id,
|
||||
worker,
|
||||
param,
|
||||
callback
|
||||
callback,
|
||||
allowInterrupt,
|
||||
enableTimeOut,
|
||||
time,
|
||||
unit
|
||||
);
|
||||
wrapper.setDependWrappers(new LinkedHashSet<>());
|
||||
wrapper.setNextWrappers(new LinkedHashSet<>());
|
||||
@@ -309,6 +331,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
wrapper.getWrapperStrategy().setDependMustStrategyMapper(new DependMustStrategyMapper()
|
||||
.addDependMust(mustDependSet));
|
||||
}
|
||||
//noinspection deprecation
|
||||
wrapper.getWrapperStrategy().setDependenceStrategy(DependenceStrategy.IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY);
|
||||
} else {
|
||||
if (mustDependSet != null && mustDependSet.size() > 0) {
|
||||
@@ -320,7 +343,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
wrapper.getWrapperStrategy().setDependenceStrategy(dependenceStrategy);
|
||||
}
|
||||
if (dependWrapperActionStrategyMap != null && dependWrapperActionStrategyMap.size() > 0) {
|
||||
DependWrapperStrategyMapper mapper = new DependWrapperStrategyMapper();
|
||||
DependOnUpWrapperStrategyMapper mapper = new DependOnUpWrapperStrategyMapper();
|
||||
dependWrapperActionStrategyMap.forEach(mapper::putMapping);
|
||||
wrapper.getWrapperStrategy().setDependWrapperStrategyMapper(mapper);
|
||||
}
|
||||
@@ -329,8 +352,14 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
.ifPresent(mustMapper -> mustMapper.addDependMust(wrapper)));
|
||||
}
|
||||
if (selfIsSpecialMap != null && selfIsSpecialMap.size() > 0) {
|
||||
selfIsSpecialMap.forEach((next, strategy) -> Optional.ofNullable(next.getWrapperStrategy().getDependWrapperStrategyMapper())
|
||||
.ifPresent(wrapperMapper -> wrapperMapper.putMapping(wrapper, strategy)));
|
||||
selfIsSpecialMap.forEach((next, strategy) -> {
|
||||
DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper();
|
||||
if (dependOnUpWrapperStrategyMapper == null) {
|
||||
next.getWrapperStrategy().setDependWrapperStrategyMapper(
|
||||
dependOnUpWrapperStrategyMapper = new DependOnUpWrapperStrategyMapper());
|
||||
}
|
||||
dependOnUpWrapperStrategyMapper.putMapping(wrapper, strategy);
|
||||
});
|
||||
}
|
||||
}
|
||||
// ========== 设置检查是否跳过策略 ==========
|
||||
@@ -344,18 +373,9 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
wrapper.getWrapperStrategy().setSkipStrategy(skipStrategy);
|
||||
}
|
||||
}
|
||||
// ========== 设置单wrapper超时检查 ==========
|
||||
{
|
||||
if (enableTimeOut) {
|
||||
if (time <= 0) {
|
||||
throw new IllegalStateException("timeout time " + time + " must > " + 0);
|
||||
}
|
||||
if (unit == null) {
|
||||
throw new IllegalStateException("timeout unit must not null");
|
||||
}
|
||||
wrapper.setTimeOut(new WorkerWrapper.TimeOutProperties(true, time, unit, allowInterrupt, wrapper));
|
||||
}
|
||||
}
|
||||
// ========== end ==========
|
||||
wrapper.state.set(WorkerWrapper.State.INIT.id);
|
||||
wrapper.setParam(param);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@@ -363,8 +383,8 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
|
||||
/**
|
||||
* @deprecated 建议使用 {@link WorkerWrapperBuilder#depends(WorkerWrapper[])}
|
||||
* 或{@link WorkerWrapperBuilder#setDepend()}设置更多选项,例如{@link WorkerWrapperBuilder.SetDepend#wrapper(WorkerWrapper[])}
|
||||
* 如果是想要“必须依赖”的功能,则使用{@link WorkerWrapperBuilder.SetDepend#mustRequireWrapper(WorkerWrapper[])}
|
||||
* 或{@link WorkerWrapperBuilder#setDepend()}设置更多选项,例如{@link SetDepend#wrapper(WorkerWrapper[])}
|
||||
* 如果是想要“必须依赖”的功能,则使用{@link SetDepend#mustRequireWrapper(WorkerWrapper[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public BUILDER_SUB_CLASS depend(WorkerWrapper<?, ?>... wrappers) {
|
||||
@@ -379,8 +399,8 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
|
||||
/**
|
||||
* @deprecated 建议使用 {@link WorkerWrapperBuilder#depends(WorkerWrapper[])}。
|
||||
* 或{@link WorkerWrapperBuilder#setDepend()}设置更多选项,例如{@link WorkerWrapperBuilder.SetDepend#wrapper(WorkerWrapper)}
|
||||
* 如果是想要“必须依赖”的功能,则使用{@link WorkerWrapperBuilder.SetDepend#mustRequireWrapper(WorkerWrapper[])}
|
||||
* 或{@link WorkerWrapperBuilder#setDepend()}设置更多选项,例如{@link SetDepend#wrapper(WorkerWrapper)}
|
||||
* 如果是想要“必须依赖”的功能,则使用{@link SetDepend#mustRequireWrapper(WorkerWrapper[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public BUILDER_SUB_CLASS depend(WorkerWrapper<?, ?> wrapper) {
|
||||
@@ -388,7 +408,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 建议使用 {@link WorkerWrapperBuilder.SetDepend#requireWrapper(WorkerWrapper, boolean)}}
|
||||
* @deprecated 建议使用 {@link SetDepend#requireWrapper(WorkerWrapper, boolean)}}
|
||||
*/
|
||||
@Deprecated
|
||||
public BUILDER_SUB_CLASS depend(WorkerWrapper<?, ?> wrapper, boolean isMust) {
|
||||
@@ -459,7 +479,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
* 默认为true。
|
||||
*
|
||||
* @param needCheckNextWrapperResult 设为true后,如果之后的Wrapper已经执行完毕。
|
||||
* 则跳过本Wrapper并设置{@link WorkResult#getEx()}为{@link com.jd.platform.async.exception.SkippedException}。
|
||||
* 则跳过本Wrapper并设置{@link WorkResult#getEx()}为{@link SkippedException}。
|
||||
* @deprecated v1.5中已经废弃。请使用
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -471,6 +491,7 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
// util method
|
||||
|
||||
private BUILDER_SUB_CLASS returnThisBuilder() {
|
||||
//noinspection unchecked
|
||||
return (BUILDER_SUB_CLASS) this;
|
||||
}
|
||||
|
||||
786
asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java
Executable file
786
asyncTool-core/src/main/java/com/jd/platform/async/wrapper/WorkerWrapper.java
Executable file
@@ -0,0 +1,786 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.exception.CancelSkippedException;
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.callback.DefaultCallback;
|
||||
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.wrapper.strategy.WrapperStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceAction;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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.AFTER_WORK;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.BUILDING;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.ERROR;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.INIT;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.SKIP;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.STARTED;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.SUCCESS;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.WORKING;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_all;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_beforeWorkingEnd;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_notWorked;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.states_of_skipOrAfterWork;
|
||||
import static com.jd.platform.async.wrapper.WorkerWrapper.State.*;
|
||||
|
||||
/**
|
||||
* 对每个worker及callback进行包装,一对一
|
||||
* <p/>
|
||||
* v1.5时将其抽取为抽象类,以解耦并提高扩展性。
|
||||
*
|
||||
* @author wuweifeng wrote on 2019-11-19.
|
||||
*/
|
||||
@SuppressWarnings("AlibabaAbstractClassShouldStartWithAbstractNaming")
|
||||
public abstract class WorkerWrapper<T, V> {
|
||||
// ========== 固定属性 ==========
|
||||
|
||||
/**
|
||||
* 该wrapper的唯一标识
|
||||
*/
|
||||
protected final String id;
|
||||
protected final IWorker<T, V> worker;
|
||||
protected final ICallback<T, V> callback;
|
||||
/**
|
||||
* 各种策略的封装类。
|
||||
*/
|
||||
private final WrapperStrategy wrapperStrategy;
|
||||
/**
|
||||
* 是否允许被打断
|
||||
*/
|
||||
protected final boolean allowInterrupt;
|
||||
/**
|
||||
* 是否启动超时检查
|
||||
*/
|
||||
final boolean enableTimeout;
|
||||
/**
|
||||
* 超时时间长度
|
||||
*/
|
||||
final long timeoutLength;
|
||||
/**
|
||||
* 超时时间单位
|
||||
*/
|
||||
final TimeUnit timeoutUnit;
|
||||
|
||||
// ========== 临时属性 ==========
|
||||
|
||||
/**
|
||||
* worker将来要处理的param
|
||||
*/
|
||||
protected volatile T param;
|
||||
/**
|
||||
* 原子设置wrapper的状态
|
||||
* <p>
|
||||
* {@link State}此枚举类枚举了state值所代表的状态枚举。
|
||||
*/
|
||||
protected final AtomicInteger state = new AtomicInteger(BUILDING.id);
|
||||
/**
|
||||
* 该值将在{@link IWorker#action(Object, Map)}进行时设为当前线程,在任务开始前或结束后都为null。
|
||||
*/
|
||||
protected final AtomicReference<Thread> doWorkingThread = new AtomicReference<>();
|
||||
/**
|
||||
* 也是个钩子变量,用来存临时的结果
|
||||
*/
|
||||
protected final AtomicReference<WorkResult<V>> workResult = new AtomicReference<>(null);
|
||||
|
||||
WorkerWrapper(String id,
|
||||
IWorker<T, V> worker,
|
||||
ICallback<T, V> callback,
|
||||
boolean allowInterrupt,
|
||||
boolean enableTimeout,
|
||||
long timeoutLength,
|
||||
TimeUnit timeoutUnit,
|
||||
WrapperStrategy wrapperStrategy
|
||||
) {
|
||||
if (worker == null) {
|
||||
throw new NullPointerException("async.worker is null");
|
||||
}
|
||||
this.worker = worker;
|
||||
this.id = id;
|
||||
//允许不设置回调
|
||||
if (callback == null) {
|
||||
callback = new DefaultCallback<>();
|
||||
}
|
||||
this.callback = callback;
|
||||
this.allowInterrupt = allowInterrupt;
|
||||
this.enableTimeout = enableTimeout;
|
||||
this.timeoutLength = timeoutLength;
|
||||
this.timeoutUnit = timeoutUnit;
|
||||
this.wrapperStrategy = wrapperStrategy;
|
||||
}
|
||||
|
||||
WorkerWrapper(String id,
|
||||
IWorker<T, V> worker,
|
||||
ICallback<T, V> callback,
|
||||
boolean allowInterrupt,
|
||||
boolean enableTimeout,
|
||||
long timeoutLength,
|
||||
TimeUnit timeoutUnit) {
|
||||
this(id, worker, callback, allowInterrupt, enableTimeout, timeoutLength, timeoutUnit, new StableWrapperStrategy());
|
||||
}
|
||||
|
||||
// ========== public ==========
|
||||
|
||||
/**
|
||||
* 外部调用本线程运行此wrapper的入口方法。
|
||||
* 该方法将会确定这组wrapper所属的group。
|
||||
*
|
||||
* @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。
|
||||
* @param remainTime 剩下的时间
|
||||
* @param group wrapper组
|
||||
* @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。
|
||||
*/
|
||||
public void work(ExecutorService executorService,
|
||||
long remainTime,
|
||||
WorkerWrapperGroup group) {
|
||||
work(executorService, null, remainTime, group);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回{@link #workResult}的值。
|
||||
* 若调用此方法时workResult还未设置,将会返回{@link WorkResult#defaultResult()}。
|
||||
*/
|
||||
public WorkResult<V> getWorkResult() {
|
||||
WorkResult<V> res = workResult.get();
|
||||
return res == null ? WorkResult.defaultResult() : res;
|
||||
}
|
||||
|
||||
public void setParam(T param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return of(state.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下游Wrapper
|
||||
*/
|
||||
public abstract Set<WorkerWrapper<?, ?>> getNextWrappers();
|
||||
|
||||
/**
|
||||
* 获取上游wrapper
|
||||
*/
|
||||
public abstract Set<WorkerWrapper<?, ?>> getDependWrappers();
|
||||
|
||||
/**
|
||||
* 获取本wrapper的超时情况。如有必要还会修改wrapper状态。
|
||||
*
|
||||
* @param withEndIt 如果为true,在检查出已经超时的时候,会将其快速结束。
|
||||
* @param startTime 起始时间
|
||||
* @param totalTimeLength 总任务时长
|
||||
* @return 超时返回-1L,结束但未超时返回0L,尚未结束且未超时返回与deadline的差值
|
||||
* <p>
|
||||
* 当没有超时,若该wrapper已经结束但没有超时,返回 0L 。
|
||||
* <p>
|
||||
* 如果该wrapper单独了设置超时策略并正在运行,返回距离超时策略限时相差的毫秒值。
|
||||
* 例如设置10ms超时,此时已经开始3ms,则返回 7L。
|
||||
* 如果此差值<1,则返回 1L。
|
||||
* <p>
|
||||
* 如果已经超时,则返回 -1L。
|
||||
* </p>
|
||||
*/
|
||||
public long checkTimeout(boolean withEndIt, long startTime, long totalTimeLength) {
|
||||
do {
|
||||
WorkResult<V> _workResult = workResult.get();
|
||||
// 如果已经有结果了,就根据结果值判断
|
||||
if (_workResult != null) {
|
||||
return _workResult.getResultState() == ResultState.TIMEOUT ? -1L : 0L;
|
||||
}
|
||||
// 如果还没有出结果
|
||||
// 判断是否超时
|
||||
long now = SystemClock.now();
|
||||
if (totalTimeLength < now - startTime
|
||||
|| enableTimeout && timeoutUnit.toMillis(timeoutLength) < now - startTime) {
|
||||
// 如果需要处理该wrapper的状态
|
||||
if (withEndIt) {
|
||||
// CAS一个超时的结果
|
||||
if (!workResult.compareAndSet(
|
||||
null,
|
||||
new WorkResult<>(null, ResultState.TIMEOUT, null))
|
||||
) {
|
||||
// 就在想CAS的时候,出结果了,就采用新的结果重新判断一次
|
||||
continue;
|
||||
}
|
||||
fastFail(true, null, false);
|
||||
}
|
||||
return -1L;
|
||||
}
|
||||
// 正在运行,尚未超时
|
||||
else {
|
||||
return Math.max(1, startTime + totalTimeLength - now);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接取消wrapper运行。
|
||||
* 如果状态在 {@link State#states_of_beforeWorkingEnd}中,则调用 {@link #fastFail(boolean, Exception, boolean)}。
|
||||
*/
|
||||
public void cancel() {
|
||||
if (State.setState(state, states_of_beforeWorkingEnd, SKIP, null)) {
|
||||
fastFail(false, new CancelSkippedException(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public WrapperStrategy getWrapperStrategy() {
|
||||
return wrapperStrategy;
|
||||
}
|
||||
|
||||
// ========== protected ==========
|
||||
|
||||
/**
|
||||
* 工作的核心方法。
|
||||
*
|
||||
* @param fromWrapper 代表这次work是由哪个上游wrapper发起的。如果是首个Wrapper则为null。
|
||||
* @param remainTime 剩余时间。
|
||||
* @throws IllegalStateException 当wrapper正在building状态时被启动,则会抛出该异常。
|
||||
*/
|
||||
protected void work(ExecutorService executorService,
|
||||
WorkerWrapper<?, ?> fromWrapper,
|
||||
long remainTime,
|
||||
WorkerWrapperGroup group
|
||||
) {
|
||||
long now = SystemClock.now();
|
||||
// ================================================
|
||||
// 以下是一些lambda。
|
||||
// 因为抽取成方法反而不好传参、污染类方法,所以就这么干了
|
||||
final Consumer<Boolean> __function__callbackResult =
|
||||
success -> {
|
||||
WorkResult<V> _workResult = getWorkResult();
|
||||
try {
|
||||
callback.result(success, param, _workResult);
|
||||
} catch (Exception e) {
|
||||
if (setState(state, states_of_skipOrAfterWork, ERROR, null)) {
|
||||
fastFail(false, e, _workResult.getEx() instanceof SkippedException);
|
||||
}
|
||||
}
|
||||
};
|
||||
final Runnable __function__callbackResultOfFalse_beginNext =
|
||||
() -> {
|
||||
__function__callbackResult.accept(false);
|
||||
beginNext(executorService, now, remainTime, group);
|
||||
};
|
||||
final BiConsumer<Boolean, Exception> __function__fastFail_callbackResult$false_beginNext =
|
||||
(fastFail_isTimeout, fastFail_exception) -> {
|
||||
boolean isSkip = fastFail_exception instanceof SkippedException;
|
||||
fastFail(fastFail_isTimeout && !isSkip, fastFail_exception, isSkip);
|
||||
__function__callbackResultOfFalse_beginNext.run();
|
||||
};
|
||||
final Runnable __function__doWork =
|
||||
() -> {
|
||||
if (setState(state, STARTED, WORKING)) {
|
||||
try {
|
||||
fire(group);
|
||||
} catch (Exception e) {
|
||||
if (setState(state, WORKING, ERROR)) {
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(false, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (setState(state, WORKING, AFTER_WORK)) {
|
||||
__function__callbackResult.accept(true);
|
||||
beginNext(executorService, now, remainTime, group);
|
||||
}
|
||||
};
|
||||
// ================================================
|
||||
// 开始执行
|
||||
try {
|
||||
if (isState(state, BUILDING)) {
|
||||
throw new IllegalStateException("wrapper can't work because state is BUILDING ! wrapper is " + this);
|
||||
}
|
||||
// 判断是否整组取消
|
||||
if (group.isWaitingCancel() || group.isCancelled()) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
// 总的已经超时了,就快速失败,进行下一个
|
||||
if (remainTime <= 0) {
|
||||
if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) {
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(true, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 如果自己已经执行过了。
|
||||
// 可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了
|
||||
final AtomicReference<State> oldStateRef = new AtomicReference<>(null);
|
||||
if (!setState(state, states_of_notWorked, STARTED, oldStateRef::set)) {
|
||||
return;
|
||||
}
|
||||
// 如果wrapper是第一次,要调用callback.begin
|
||||
if (oldStateRef.get() == INIT) {
|
||||
try {
|
||||
callback.begin();
|
||||
} catch (Exception e) {
|
||||
// callback.begin 发生异常
|
||||
if (setState(state, states_of_beforeWorkingEnd, ERROR, null)) {
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(false, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//如果fromWrapper为null,说明自己就是第一批要执行的
|
||||
if (fromWrapper == null) {
|
||||
// 首当其冲,开始工作
|
||||
__function__doWork.run();
|
||||
return;
|
||||
}
|
||||
|
||||
// 每个线程都需要判断是否要跳过自己,该方法可能会跳过正在工作的自己。
|
||||
final WrapperStrategy wrapperStrategy = getWrapperStrategy();
|
||||
if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) {
|
||||
if (setState(state, STARTED, SKIP)) {
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(false, new SkippedException());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是由其他wrapper调用而运行至此,则使用策略器决定自己的行为
|
||||
DependenceAction.WithProperty judge =
|
||||
wrapperStrategy.judgeAction(getDependWrappers(), this, fromWrapper);
|
||||
switch (judge.getDependenceAction()) {
|
||||
case TAKE_REST:
|
||||
return;
|
||||
case FAST_FAIL:
|
||||
if (setState(state, STARTED, ERROR)) {
|
||||
// 根据FAST_FAIL.fastFailException()设置的属性值来设置fastFail方法的参数
|
||||
ResultState resultState = judge.getResultState();
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(
|
||||
resultState == ResultState.TIMEOUT,
|
||||
judge.getFastFailException()
|
||||
);
|
||||
}
|
||||
return;
|
||||
case START_WORK:
|
||||
__function__doWork.run();
|
||||
return;
|
||||
case JUDGE_BY_AFTER:
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this +
|
||||
",fromWrapper=" + fromWrapper);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// wrapper本身抛出了不该有的异常
|
||||
setState(state, states_all, ERROR, null);
|
||||
NotExpectedException ex = new NotExpectedException(e, this);
|
||||
workResult.set(new WorkResult<>(null, ResultState.EXCEPTION, ex));
|
||||
__function__fastFail_callbackResult$false_beginNext.accept(false, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 本工作线程执行自己的job.
|
||||
* <p/>
|
||||
* 本方法不负责校验状态。请在调用前自行检验
|
||||
*/
|
||||
protected void fire(WorkerWrapperGroup group) {
|
||||
try {
|
||||
doWorkingThread.set(Thread.currentThread());
|
||||
//执行耗时操作
|
||||
V result = worker.action(param, group.getForParamUseWrappers());
|
||||
workResult.compareAndSet(
|
||||
null,
|
||||
new WorkResult<>(result, ResultState.SUCCESS)
|
||||
);
|
||||
} finally {
|
||||
doWorkingThread.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速失败。
|
||||
* 该方法不负责检查状态,请自行控制。
|
||||
*
|
||||
* @param isTimeout 是否是因为超时而快速失败
|
||||
* @param e 设置异常信息到{@link WorkResult#getEx()}
|
||||
*/
|
||||
protected void fastFail(boolean isTimeout, Exception e, boolean isSkip) {
|
||||
// 试图打断正在执行{@link IWorker#action(Object, Map)}的线程
|
||||
Thread _doWorkingThread;
|
||||
if ((_doWorkingThread = doWorkingThread.get()) != null
|
||||
// 不会打断自己
|
||||
&& !Objects.equals(Thread.currentThread(), _doWorkingThread)) {
|
||||
_doWorkingThread.interrupt();
|
||||
}
|
||||
// 尚未处理过结果则设置
|
||||
workResult.compareAndSet(null, new WorkResult<>(
|
||||
worker.defaultValue(),
|
||||
isTimeout ? ResultState.TIMEOUT : (isSkip ? ResultState.DEFAULT : ResultState.EXCEPTION),
|
||||
e
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行下一个任务
|
||||
* <p/>
|
||||
* 本方法不负责校验状态。请在调用前自行检验
|
||||
*/
|
||||
protected void beginNext(ExecutorService executorService, long now, long remainTime, WorkerWrapperGroup group) {
|
||||
//花费的时间
|
||||
final long costTime = SystemClock.now() - now;
|
||||
final long nextRemainTIme = remainTime - costTime;
|
||||
Set<WorkerWrapper<?, ?>> nextWrappers = getNextWrappers();
|
||||
if (nextWrappers == null) {
|
||||
PollingCenter.getInstance().checkGroup(group.new CheckFinishTask());
|
||||
return;
|
||||
}
|
||||
// nextWrappers只有一个,就用本线程继续跑。
|
||||
if (nextWrappers.size() == 1) {
|
||||
WorkerWrapper<?, ?> next = null;
|
||||
try {
|
||||
next = nextWrappers.stream().findFirst().get();
|
||||
group.addWrapper(next);
|
||||
setState(state, AFTER_WORK, SUCCESS);
|
||||
} finally {
|
||||
PollingCenter.getInstance().checkGroup(group.new CheckFinishTask());
|
||||
if (next != null) {
|
||||
next.work(executorService, this, nextRemainTIme, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
// nextWrappers有多个
|
||||
else {
|
||||
try {
|
||||
group.addWrapper(nextWrappers);
|
||||
nextWrappers.forEach(next -> executorService.submit(() ->
|
||||
next.work(executorService, this, nextRemainTIme, group))
|
||||
);
|
||||
setState(state, AFTER_WORK, SUCCESS);
|
||||
} finally {
|
||||
PollingCenter.getInstance().checkGroup(group.new CheckFinishTask());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ========== hashcode and equals ==========
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code return id.hashCode();}返回id值的hashcode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// final String id can use to .hashcode() .
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
// ========== builder ==========
|
||||
|
||||
public static <T, V> WorkerWrapperBuilder<T, V> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自v1.5,该类被抽取到{@link StableWorkerWrapperBuilder}抽象类,兼容之前的版本。
|
||||
*/
|
||||
public static class Builder<W, C> extends StableWorkerWrapperBuilder<W, C, Builder<W, C>> {
|
||||
/**
|
||||
* @deprecated 建议使用 {@link #builder()}返回{@link WorkerWrapperBuilder}接口,以调用v1.5之后的规范api
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
public Builder() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== package access methods ==========
|
||||
|
||||
abstract void setNextWrappers(Set<WorkerWrapper<?, ?>> nextWrappers);
|
||||
|
||||
abstract void setDependWrappers(Set<WorkerWrapper<?, ?>> dependWrappers);
|
||||
|
||||
// ========== toString ==========
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(400)
|
||||
.append("WorkerWrapper{id=").append(id)
|
||||
.append(", state=").append(of(state.get()))
|
||||
.append(", param=").append(param)
|
||||
.append(", workResult=").append(workResult)
|
||||
.append(", allowInterrupt=").append(allowInterrupt)
|
||||
.append(", enableTimeout=").append(enableTimeout)
|
||||
.append(", timeoutLength=").append(timeoutLength)
|
||||
.append(", timeoutUnit=").append(timeoutUnit)
|
||||
// 防止循环引用,这里只输出相关Wrapper的id
|
||||
.append(", dependWrappers::getId=[");
|
||||
final Set<WorkerWrapper<?, ?>> dependWrappers = getDependWrappers();
|
||||
dependWrappers.stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", "));
|
||||
if (dependWrappers.size() > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
sb
|
||||
.append("], nextWrappers::getId=[");
|
||||
final Set<WorkerWrapper<?, ?>> nextWrappers = getNextWrappers();
|
||||
nextWrappers.stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", "));
|
||||
if (nextWrappers.size() > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
sb
|
||||
.append("], doWorkingThread=").append(doWorkingThread.get())
|
||||
.append(", worker=").append(worker)
|
||||
.append(", callback=").append(callback)
|
||||
.append(", wrapperStrategy=").append(wrapperStrategy)
|
||||
.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个通用的策略器实现类,提供了修改的功能。并兼容之前的代码。
|
||||
*/
|
||||
public static class StableWrapperStrategy extends WrapperStrategy.AbstractWrapperStrategy {
|
||||
private DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper;
|
||||
private DependMustStrategyMapper dependMustStrategyMapper;
|
||||
private DependenceStrategy dependenceStrategy;
|
||||
private SkipStrategy skipStrategy;
|
||||
|
||||
@Override
|
||||
public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() {
|
||||
return dependOnUpWrapperStrategyMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) {
|
||||
this.dependOnUpWrapperStrategyMapper = dependOnUpWrapperStrategyMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependMustStrategyMapper getDependMustStrategyMapper() {
|
||||
return dependMustStrategyMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) {
|
||||
this.dependMustStrategyMapper = dependMustStrategyMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependenceStrategy getDependenceStrategy() {
|
||||
return dependenceStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDependenceStrategy(DependenceStrategy dependenceStrategy) {
|
||||
this.dependenceStrategy = dependenceStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipStrategy getSkipStrategy() {
|
||||
return skipStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkipStrategy(SkipStrategy skipStrategy) {
|
||||
this.skipStrategy = skipStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* state状态枚举工具类
|
||||
*/
|
||||
public enum State {
|
||||
/**
|
||||
* 初始化中,builder正在设置其数值
|
||||
*/
|
||||
BUILDING(-1),
|
||||
/**
|
||||
* 初始化完成,但是还未执行过。
|
||||
*/
|
||||
INIT(0),
|
||||
/**
|
||||
* 执行过。
|
||||
* 即至少进行了一次各种判定,例如判断 是否跳过/是否启动工作
|
||||
*/
|
||||
STARTED(1),
|
||||
/**
|
||||
* 工作状态
|
||||
*/
|
||||
WORKING(2),
|
||||
/**
|
||||
* 工作完成后的收尾工作,例如调用下游wrapper
|
||||
*/
|
||||
AFTER_WORK(3),
|
||||
/**
|
||||
* wrapper成功执行结束
|
||||
*/
|
||||
SUCCESS(4),
|
||||
/**
|
||||
* wrapper失败了
|
||||
*/
|
||||
ERROR(5),
|
||||
/**
|
||||
* wrapper被跳过
|
||||
*/
|
||||
SKIP(6);
|
||||
|
||||
// public
|
||||
|
||||
public boolean finished() {
|
||||
return this == SUCCESS || this == ERROR || this == SKIP;
|
||||
}
|
||||
|
||||
// package
|
||||
|
||||
State(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
final int id;
|
||||
|
||||
// package-static
|
||||
|
||||
static final State[] states_of_notWorked = new State[]{INIT, STARTED};
|
||||
|
||||
static final State[] states_of_skipOrAfterWork = new State[]{SKIP, AFTER_WORK};
|
||||
|
||||
static final State[] states_of_beforeWorkingEnd = new State[]{INIT, STARTED, WORKING};
|
||||
|
||||
static final State[] states_all = new State[]{BUILDING, INIT, STARTED, WORKING, AFTER_WORK, SUCCESS, ERROR, SKIP};
|
||||
|
||||
/**
|
||||
* 自旋+CAS的设置状态,如果状态不在exceptValues返回内 或 没有设置成功,则返回false。
|
||||
*
|
||||
* @param state {@link WorkerWrapper#state} 要被修改的AtomicInteger引用
|
||||
* @param exceptValues 期望的值数组,任何满足该值的state都会被修改
|
||||
* @param newValue 新值
|
||||
* @param withOperate 如果该参数不为null并且成功设置,该函数将会被执行,其参数为wrapper原子设置之前的旧状态。
|
||||
* 之所以需要这个参数,是因为当except值有多个时,无法确定是哪个值被原子修改了。
|
||||
* @return 返回是否成功设置。
|
||||
*/
|
||||
static boolean setState(AtomicInteger state,
|
||||
State[] exceptValues,
|
||||
State newValue,
|
||||
Consumer<State> withOperate) {
|
||||
int current;
|
||||
boolean inExcepts;
|
||||
while (true) {
|
||||
// 判断当前值是否在exceptValues范围内
|
||||
current = state.get();
|
||||
inExcepts = false;
|
||||
for (State exceptValue : exceptValues) {
|
||||
if (inExcepts = current == exceptValue.id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果不在 exceptValues 范围内,直接返回false。
|
||||
if (!inExcepts) {
|
||||
return false;
|
||||
}
|
||||
// 如果在 exceptValues 范围,cas成功返回true,失败(即当前值被修改)则自旋。
|
||||
if (state.compareAndSet(current, newValue.id)) {
|
||||
if (withOperate != null) {
|
||||
withOperate.accept(of(current));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自旋+CAS的设置状态,如果状态不在exceptValues返回内 或 没有设置成功自旋后不在范围内,则返回false。
|
||||
*
|
||||
* @param state {@link WorkerWrapper#state} 要被修改的AtomicInteger引用
|
||||
* @param exceptValue 期望的值
|
||||
* @param newValue 新值
|
||||
* @return 返回是否成功设置。
|
||||
*/
|
||||
static boolean setState(AtomicInteger state,
|
||||
State exceptValue,
|
||||
State newValue) {
|
||||
int current;
|
||||
// 如果当前值与期望值相同
|
||||
while ((current = state.get()) == exceptValue.id) {
|
||||
// 则尝试CAS设置新值
|
||||
if (state.compareAndSet(current, newValue.id)) {
|
||||
return true;
|
||||
}
|
||||
// 如果当前值被改变,则尝试自旋
|
||||
}
|
||||
// 如果当前值与期望值不相同了,就直接返回false
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自旋+CAS的判断是否在这些excepts范围内
|
||||
*
|
||||
* @param excepts 范围。
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
static boolean inStates(AtomicInteger state, State... excepts) {
|
||||
int current;
|
||||
boolean inExcepts;
|
||||
while (true) {
|
||||
current = state.get();
|
||||
inExcepts = false;
|
||||
for (State except : excepts) {
|
||||
if (current == except.id) {
|
||||
inExcepts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state.get() == current) {
|
||||
return inExcepts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CAS的判断是否是某个状态
|
||||
*/
|
||||
static boolean isState(AtomicInteger state, @SuppressWarnings("SameParameterValue") State except) {
|
||||
return state.compareAndSet(except.id, except.id);
|
||||
}
|
||||
|
||||
static State of(int id) {
|
||||
return id2state.get(id);
|
||||
}
|
||||
|
||||
static final Map<Integer, State> id2state;
|
||||
|
||||
static {
|
||||
HashMap<Integer, State> map = new HashMap<>();
|
||||
for (State s : State.values()) {
|
||||
map.put(s.id, s);
|
||||
}
|
||||
id2state = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 这是因未知错误而引发的异常
|
||||
*/
|
||||
public static class NotExpectedException extends Exception {
|
||||
public NotExpectedException(Throwable cause, WorkerWrapper<?, ?> wrapper) {
|
||||
super("It's should not happened Exception . wrapper is " + wrapper, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,9 @@ package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependWrapperActionStrategy;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -113,9 +112,11 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
* @param wrapper 需要设置特殊策略的Wrapper。
|
||||
* @param strategy 特殊策略。
|
||||
*/
|
||||
SetDepend<T, V> specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper<?, ?> wrapper);
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
SetDepend<T, V> specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper<?, ?> wrapper);
|
||||
|
||||
default SetDepend<T, V> specialDependWrapper(DependWrapperActionStrategy strategy, WorkerWrapper... wrappers) {
|
||||
@SuppressWarnings("unused")
|
||||
default SetDepend<T, V> specialDependWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper... wrappers) {
|
||||
if (strategy == null || wrappers == null) {
|
||||
return this;
|
||||
}
|
||||
@@ -157,6 +158,7 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
return setDepend().wrapper(wrappers).end();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
default WorkerWrapperBuilder<T, V> depends(Collection<WorkerWrapper> wrappers) {
|
||||
return setDepend().wrapper(wrappers).end();
|
||||
}
|
||||
@@ -165,6 +167,7 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
return setDepend().wrapper(wrappers).strategy(strategy).end();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
default WorkerWrapperBuilder<T, V> depends(DependenceStrategy strategy, Collection<WorkerWrapper> wrappers) {
|
||||
return setDepend().wrapper(wrappers).strategy(strategy).end();
|
||||
}
|
||||
@@ -205,6 +208,7 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
*/
|
||||
SetNext<T, V> mustToNextWrapper(WorkerWrapper<?, ?> wrapper);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
default SetNext<T, V> requireToNextWrapper(WorkerWrapper<?, ?> wrapper, boolean must) {
|
||||
return must ? mustToNextWrapper(wrapper) : wrapper(wrapper);
|
||||
}
|
||||
@@ -216,7 +220,7 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
* @param wrapper 依赖本Wrapper的下游Wrapper。
|
||||
* @return 返回Builder自身。
|
||||
*/
|
||||
SetNext<T, V> specialToNextWrapper(DependWrapperActionStrategy strategy, WorkerWrapper<?, ?> wrapper);
|
||||
SetNext<T, V> specialToNextWrapper(DependOnUpWrapperStrategy strategy, WorkerWrapper<?, ?> wrapper);
|
||||
|
||||
WorkerWrapperBuilder<T, V> end();
|
||||
}
|
||||
@@ -230,6 +234,7 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
return setNext().wrapper(wrappers).end();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
default WorkerWrapperBuilder<T, V> nextOf(Collection<WorkerWrapper> wrappers) {
|
||||
return setNext().wrapper(wrappers).end();
|
||||
}
|
||||
@@ -257,13 +262,6 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
*/
|
||||
SetTimeOut<T, V> setTime(long time, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* 是否允许被试图中断线程
|
||||
*
|
||||
* @param allow 是则true
|
||||
*/
|
||||
SetTimeOut<T, V> allowInterrupt(boolean allow);
|
||||
|
||||
WorkerWrapperBuilder<T, V> end();
|
||||
}
|
||||
|
||||
@@ -274,13 +272,20 @@ public interface WorkerWrapperBuilder<T, V> {
|
||||
* @param unit 时间单位
|
||||
*/
|
||||
default WorkerWrapperBuilder<T, V> timeout(long time, TimeUnit unit) {
|
||||
return timeout(true, time, unit, false);
|
||||
return timeout(true, time, unit);
|
||||
}
|
||||
|
||||
default WorkerWrapperBuilder<T, V> timeout(boolean enableTimeOut, long time, TimeUnit unit, boolean allowInterrupt) {
|
||||
return setTimeOut().enableTimeOut(enableTimeOut).setTime(time, unit).allowInterrupt(allowInterrupt).end();
|
||||
default WorkerWrapperBuilder<T, V> timeout(boolean enableTimeOut, long time, TimeUnit unit) {
|
||||
return setTimeOut().enableTimeOut(enableTimeOut).setTime(time, unit).end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许被试图中断线程
|
||||
*
|
||||
* @param allow 是则true
|
||||
*/
|
||||
WorkerWrapperBuilder<T, V> allowInterrupt(boolean allow);
|
||||
|
||||
/**
|
||||
* 构建Wrapper。
|
||||
*
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.exception.CancelSkippedException;
|
||||
import com.jd.platform.async.executor.PollingCenter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
import com.jd.platform.async.openutil.timer.*;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/9-下午7:21
|
||||
*/
|
||||
public class WorkerWrapperGroup {
|
||||
/**
|
||||
* 任务开始时间
|
||||
*/
|
||||
private final long groupStartTime;
|
||||
/**
|
||||
* 任务限时
|
||||
*/
|
||||
private final long timeoutLength;
|
||||
/**
|
||||
* 该map存放所有wrapper的id和wrapper映射
|
||||
* <p/>
|
||||
* 需要线程安全。
|
||||
*/
|
||||
private final Map<String, WorkerWrapper<?, ?>> forParamUseWrappers = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 当全部wrapper都调用结束,它会countDown
|
||||
*/
|
||||
private final CountDownLatch endCDL = new CountDownLatch(1);
|
||||
/**
|
||||
* 检测到超时,此标记变量将为true。
|
||||
*/
|
||||
private final AtomicBoolean anyTimeout = new AtomicBoolean(false);
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private volatile long finishTime = -1L;
|
||||
/**
|
||||
* 取消任务状态
|
||||
* 0 - not cancel , 1 - waiting cancel , 2 - already cancel
|
||||
*/
|
||||
private final AtomicInteger cancelState = new AtomicInteger();
|
||||
|
||||
public static final int NOT_CANCEL = 0;
|
||||
public static final int WAITING_CANCEL = 1;
|
||||
public static final int ALREADY_CANCEL = 2;
|
||||
|
||||
public WorkerWrapperGroup(long groupStartTime, long timeoutLength) {
|
||||
this.groupStartTime = groupStartTime;
|
||||
this.timeoutLength = timeoutLength;
|
||||
}
|
||||
|
||||
public void addWrapper(Collection<? extends WorkerWrapper<?, ?>> 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<String, WorkerWrapper<?, ?>> getForParamUseWrappers() {
|
||||
return forParamUseWrappers;
|
||||
}
|
||||
|
||||
public CountDownLatch getEndCDL() {
|
||||
return endCDL;
|
||||
}
|
||||
|
||||
public long getGroupStartTime() {
|
||||
return groupStartTime;
|
||||
}
|
||||
|
||||
public AtomicBoolean getAnyTimeout() {
|
||||
return anyTimeout;
|
||||
}
|
||||
|
||||
public long getFinishTime() {
|
||||
return finishTime;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelState.get() == ALREADY_CANCEL;
|
||||
}
|
||||
|
||||
public boolean isWaitingCancel() {
|
||||
return cancelState.get() == WAITING_CANCEL;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public boolean pleaseCancel() {
|
||||
return cancelState.compareAndSet(NOT_CANCEL, WAITING_CANCEL);
|
||||
}
|
||||
|
||||
public class CheckFinishTask implements TimerTask {
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public void run(Timeout timeout) throws Exception {
|
||||
// 已经完成了
|
||||
if (endCDL.getCount() < 1) {
|
||||
return;
|
||||
}
|
||||
AtomicBoolean hasTimeout = new AtomicBoolean(false);
|
||||
// 记录正在运行中的wrapper里,最近的限时时间。
|
||||
final AtomicLong minDaley = new AtomicLong(Long.MAX_VALUE);
|
||||
final Collection<WorkerWrapper<?, ?>> values = forParamUseWrappers.values();
|
||||
final Stream<WorkerWrapper<?, ?>> stream = values.size() > 128 ? values.parallelStream() : values.stream();
|
||||
final boolean needCancel = cancelState.get() == WAITING_CANCEL;
|
||||
boolean allFinish_and_notNeedCancel = stream
|
||||
// 需要取消的话就取消
|
||||
.peek(wrapper -> {
|
||||
if (needCancel) {
|
||||
wrapper.cancel();
|
||||
}
|
||||
})
|
||||
// 检查超时并保存最近一次限时时间
|
||||
// 当需要取消时,才会不断遍历。如果不需要取消,则计算一次(或并行流中多次)就因allMatch不满足而退出了。
|
||||
.peek(wrapper -> {
|
||||
// time_diff :
|
||||
// -1 -> already timeout ;
|
||||
// 0 -> finish but not timeout ;
|
||||
// X>0 -> is running , may timeout in X seconds .
|
||||
long time_diff = wrapper.checkTimeout(true, groupStartTime, timeoutLength);
|
||||
if (time_diff < 0) {
|
||||
hasTimeout.set(true);
|
||||
}
|
||||
if (time_diff == 0) {
|
||||
return;
|
||||
}
|
||||
// use CAS and SPIN for thread safety in parallelStream .
|
||||
do {
|
||||
long getMinDaley = minDaley.get();
|
||||
// 需要设置最小时间,但是cas失败,则自旋
|
||||
if (getMinDaley <= time_diff && !minDaley.compareAndSet(getMinDaley, time_diff)) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
} while (true);
|
||||
})
|
||||
// 判断是否不需要取消且全部结束
|
||||
// 在不需要取消时,这里如果还有未结束的wrapper则会提前结束流并返回false
|
||||
// 在需要取消时,会全部遍历一遍并取消掉已经进入链路的wrapper
|
||||
.allMatch(wrapper -> !needCancel && wrapper.getState().finished());
|
||||
long getMinDaley = minDaley.get();
|
||||
// 如果本次取消掉了任务,或是所有wrapper都已经完成
|
||||
// ( ps : 前后两条件在这里是必定 一真一假 或 两者全假 )
|
||||
if (needCancel || allFinish_and_notNeedCancel) {
|
||||
// 如果这次进行了取消,则设置取消状态为已完成
|
||||
if (needCancel) {
|
||||
cancelState.set(ALREADY_CANCEL);
|
||||
}
|
||||
anyTimeout.set(hasTimeout.get());
|
||||
finishTime = SystemClock.now();
|
||||
endCDL.countDown();
|
||||
}
|
||||
// 如果有正在运行的wrapper
|
||||
else {
|
||||
// 如果有正在WORKING的wrapper,则计算一下限时时间,限时完成后轮询它。
|
||||
if (getMinDaley != Long.MAX_VALUE) {
|
||||
PollingCenter.getInstance().checkGroup(this, getMinDaley);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hashCode and equals will called WorkerWrapperGroup.this
|
||||
|
||||
/**
|
||||
* 将会调用{@link WorkerWrapperGroup#hashCode()}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return WorkerWrapperGroup.this.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将会调用{@link WorkerWrapperGroup#equals(Object)}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CheckFinishTask)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(WorkerWrapperGroup.this, ((CheckFinishTask) obj).getParent());
|
||||
}
|
||||
|
||||
private WorkerWrapperGroup getParent() {
|
||||
return WorkerWrapperGroup.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.jd.platform.async.wrapper.strategy;
|
||||
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependOnUpWrapperStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceAction;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/17-下午6:23
|
||||
*/
|
||||
public interface WrapperStrategy extends DependenceStrategy, SkipStrategy {
|
||||
// ========== 这三个策略器用于链式判断是否要开始工作 ==========
|
||||
|
||||
// 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy
|
||||
|
||||
/**
|
||||
* 设置对特殊Wrapper专用的依赖响应策略。
|
||||
*
|
||||
* @return 该值允许为null
|
||||
*/
|
||||
DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper();
|
||||
|
||||
/**
|
||||
* 对必须完成的(must的)Wrapper的依赖响应策略。
|
||||
* 这是一个不得不向历史妥协的属性。用于适配must开关方式。
|
||||
*
|
||||
* @return 该值允许为null
|
||||
* @deprecated 不推荐使用,很有可能被遗弃
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
default DependMustStrategyMapper getDependMustStrategyMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 底层全局策略。
|
||||
*
|
||||
* @return 该值不允许为null
|
||||
*/
|
||||
DependenceStrategy getDependenceStrategy();
|
||||
|
||||
// ========== 这是跳过策略 ==========
|
||||
|
||||
/**
|
||||
* 跳过策略
|
||||
*
|
||||
* @return 不允许为null
|
||||
*/
|
||||
SkipStrategy getSkipStrategy();
|
||||
|
||||
@Override
|
||||
default DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
// 如果存在依赖,则调用三层依赖响应策略进行判断
|
||||
|
||||
DependenceStrategy strategy = getDependWrapperStrategyMapper();
|
||||
if (getDependMustStrategyMapper() != null) {
|
||||
strategy = strategy == null ? getDependMustStrategyMapper() : strategy.thenJudge(getDependenceStrategy());
|
||||
}
|
||||
if (getDependenceStrategy() != null) {
|
||||
strategy = strategy == null ? getDependenceStrategy() : strategy.thenJudge(getDependenceStrategy());
|
||||
}
|
||||
if (strategy == null) {
|
||||
throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!");
|
||||
}
|
||||
return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean shouldSkip(Set<WorkerWrapper<?, ?>> nextWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper) {
|
||||
return getSkipStrategy() != null && getSkipStrategy().shouldSkip(nextWrappers, thisWrapper, fromWrapper);
|
||||
}
|
||||
|
||||
default void setDependWrapperStrategyMapper(DependOnUpWrapperStrategyMapper dependOnUpWrapperStrategyMapper) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default void setDependenceStrategy(DependenceStrategy dependenceStrategy) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default void setSkipStrategy(SkipStrategy skipStrategy) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽象策略器,实现了toString
|
||||
*/
|
||||
abstract class AbstractWrapperStrategy implements WrapperStrategy {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrapperStrategy{" +
|
||||
"dependWrapperStrategyMapper=" + getDependWrapperStrategyMapper() +
|
||||
", dependMustStrategyMapper=" + getDependMustStrategyMapper() +
|
||||
", dependenceStrategy=" + getDependenceStrategy() +
|
||||
", skipStrategy=" + getSkipStrategy() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认策略器,用默认值实现了所有属性。
|
||||
*/
|
||||
class DefaultWrapperStrategy extends AbstractWrapperStrategy {
|
||||
@Override
|
||||
public DependOnUpWrapperStrategyMapper getDependWrapperStrategyMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependMustStrategyMapper getDependMustStrategyMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependenceStrategy getDependenceStrategy() {
|
||||
return DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipStrategy getSkipStrategy() {
|
||||
return SkipStrategy.CHECK_ONE_LEVEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.jd.platform.async.wrapper.actionstrategy;
|
||||
package com.jd.platform.async.wrapper.strategy.depend;
|
||||
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
@@ -26,7 +26,7 @@ public class DependMustStrategyMapper implements DependenceStrategy {
|
||||
* 如果所有的Wrapper已经完成,本Wrapper将会开始工作。
|
||||
* <p>
|
||||
* 如果任一{@link #mustDependSet}中的Wrapper失败,则返回{@link DependenceAction#FAST_FAIL}。
|
||||
* 具体超时/异常则根据{@link com.jd.platform.async.worker.ResultState}的值进行判断。
|
||||
* 具体超时/异常则根据{@link ResultState}的值进行判断。
|
||||
* <p>
|
||||
* 如果存在Wrapper未完成 且 所有的Wrapper都未失败,则返回{@link DependenceAction#JUDGE_BY_AFTER}。
|
||||
* </p>
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.jd.platform.async.wrapper.actionstrategy;
|
||||
package com.jd.platform.async.wrapper.strategy.depend;
|
||||
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
|
||||
/**
|
||||
* 单参数策略。
|
||||
* 由上游wrapper决定本wrapper行为的单参数策略。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/1-下午11:16
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DependWrapperActionStrategy {
|
||||
public interface DependOnUpWrapperStrategy {
|
||||
/**
|
||||
* 仅使用一个参数的判断方法
|
||||
* 仅使用一个参数(即调用自身的上游wrapper)的判断方法
|
||||
*
|
||||
* @param fromWrapper 调用本Wrapper的上游Wrapper
|
||||
* @return 返回 {@link DependenceAction.WithProperty}
|
||||
@@ -24,7 +24,7 @@ public interface DependWrapperActionStrategy {
|
||||
* 未运行时,休息。
|
||||
* 失败时,失败。
|
||||
*/
|
||||
DependWrapperActionStrategy SUCCESS_CONTINUE = new DependWrapperActionStrategy() {
|
||||
DependOnUpWrapperStrategy SUCCESS_CONTINUE = new DependOnUpWrapperStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judge(WorkerWrapper<?, ?> ww) {
|
||||
switch (ww.getWorkResult().getResultState()) {
|
||||
@@ -50,7 +50,7 @@ public interface DependWrapperActionStrategy {
|
||||
* 未运行时,交给下一个策略器判断。
|
||||
* 失败时,失败。
|
||||
*/
|
||||
DependWrapperActionStrategy SUCCESS_START_INIT_CONTINUE = new DependWrapperActionStrategy() {
|
||||
DependOnUpWrapperStrategy SUCCESS_START_INIT_CONTINUE = new DependOnUpWrapperStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judge(WorkerWrapper<?, ?> ww) {
|
||||
switch (ww.getWorkResult().getResultState()) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.jd.platform.async.wrapper.actionstrategy;
|
||||
package com.jd.platform.async.wrapper.strategy.depend;
|
||||
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
|
||||
@@ -10,12 +10,12 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* 对不同的{@link WorkerWrapper}调用者实行个性化依赖响应策略。
|
||||
* <p/>
|
||||
* 使用{@link DependWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强,
|
||||
* 使用{@link DependOnUpWrapperStrategyMapper}本实现类对{@link DependenceStrategy}进行增强,
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/1-下午11:12
|
||||
*/
|
||||
public class DependWrapperStrategyMapper implements DependenceStrategy {
|
||||
private final Map<WorkerWrapper<?, ?>, DependWrapperActionStrategy> mapper = new ConcurrentHashMap<>(4);
|
||||
public class DependOnUpWrapperStrategyMapper implements DependenceStrategy {
|
||||
private final Map<WorkerWrapper<?, ?>, DependOnUpWrapperStrategy> mapper = new ConcurrentHashMap<>(4);
|
||||
|
||||
/**
|
||||
* 设置对应策略
|
||||
@@ -24,7 +24,8 @@ public class DependWrapperStrategyMapper implements DependenceStrategy {
|
||||
* @param strategy 要设置的策略
|
||||
* @return 返回this,链式调用。
|
||||
*/
|
||||
public DependWrapperStrategyMapper putMapping(WorkerWrapper<?, ?> targetWrapper, DependWrapperActionStrategy strategy) {
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public DependOnUpWrapperStrategyMapper putMapping(WorkerWrapper<?, ?> targetWrapper, DependOnUpWrapperStrategy strategy) {
|
||||
mapper.put(targetWrapper, strategy);
|
||||
toStringCache = null;
|
||||
return this;
|
||||
@@ -33,7 +34,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy {
|
||||
/**
|
||||
* 判断方法。
|
||||
* <p/>
|
||||
* 如果fromWrapper在{@link #mapper}中,则返回{@link DependWrapperActionStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER}
|
||||
* 如果fromWrapper在{@link #mapper}中,则返回{@link DependOnUpWrapperStrategy}的判断返回值。否则返回{@link DependenceAction#JUDGE_BY_AFTER}
|
||||
*
|
||||
* @param dependWrappers (这里不会使用该值)thisWrapper.dependWrappers的属性值。
|
||||
* @param thisWrapper (这里不会使用该值)thisWrapper,即为“被催促”的WorkerWrapper
|
||||
@@ -44,7 +45,7 @@ public class DependWrapperStrategyMapper implements DependenceStrategy {
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
DependWrapperActionStrategy strategy = mapper.get(fromWrapper);
|
||||
DependOnUpWrapperStrategy strategy = mapper.get(fromWrapper);
|
||||
if (strategy == null) {
|
||||
return DependenceAction.JUDGE_BY_AFTER.emptyProperty();
|
||||
}
|
||||
@@ -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)}的责任链设计模式,该返回值的意义就是调用责任链上下一个策略。
|
||||
* <p/>
|
||||
* 在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;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.jd.platform.async.wrapper.actionstrategy;
|
||||
package com.jd.platform.async.wrapper.strategy.depend;
|
||||
|
||||
import com.jd.platform.async.wrapper.WrapperEndingInspector;
|
||||
import com.jd.platform.async.exception.SkippedException;
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -36,12 +38,12 @@ public interface DependenceStrategy {
|
||||
* @param fromWrapper 调用来源Wrapper。
|
||||
* <p>
|
||||
* 该参数不会为null。
|
||||
* 因为在{@link WorkerWrapper#work(ExecutorService, long, Map, WrapperEndingInspector)}方法中传入的的第一批无依赖的Wrapper,
|
||||
* 因为在{@link WorkerWrapper#work(ExecutorService, long, WorkerWrapperGroup)}方法中传入的的第一批无依赖的Wrapper,
|
||||
* 不会被该策略器所判断,而是不论如何直接执行。
|
||||
* </p>
|
||||
* @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty}
|
||||
*/
|
||||
DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper);
|
||||
|
||||
@@ -55,7 +57,7 @@ public interface DependenceStrategy {
|
||||
DependenceStrategy that = this;
|
||||
return new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
DependenceAction.WithProperty judge = that.judgeAction(dependWrappers, thisWrapper, fromWrapper);
|
||||
@@ -81,7 +83,7 @@ public interface DependenceStrategy {
|
||||
*/
|
||||
DependenceStrategy ALL_DEPENDENCIES_ALL_SUCCESS = new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
boolean hasWaiting = false;
|
||||
@@ -119,7 +121,7 @@ public interface DependenceStrategy {
|
||||
*/
|
||||
DependenceStrategy ALL_DEPENDENCIES_ANY_SUCCESS = new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
boolean hasFailed = false;
|
||||
@@ -136,6 +138,7 @@ public interface DependenceStrategy {
|
||||
case EXCEPTION:
|
||||
resultState = !hasFailed ? workResult.getResultState() : resultState;
|
||||
fastFailException = !hasFailed ? workResult.getEx() : fastFailException;
|
||||
// 跳过不算失败
|
||||
hasFailed = true;
|
||||
break;
|
||||
default:
|
||||
@@ -155,11 +158,13 @@ public interface DependenceStrategy {
|
||||
};
|
||||
|
||||
/**
|
||||
* 如果被依赖的工作中任一失败,则立即失败。否则就开始工作(不论之前的工作有没有开始)。
|
||||
* 如果被依赖的工作中任一失败,则立即失败。
|
||||
* 否则就开始工作(不论之前的工作有没有开始)。
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
DependenceStrategy ALL_DEPENDENCIES_NONE_FAILED = new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
for (WorkerWrapper<?, ?> dependWrapper : dependWrappers) {
|
||||
@@ -167,7 +172,7 @@ public interface DependenceStrategy {
|
||||
switch (workResult.getResultState()) {
|
||||
case TIMEOUT:
|
||||
case EXCEPTION:
|
||||
return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx());
|
||||
return DependenceAction.FAST_FAIL.fastFailException(workResult.getResultState(), workResult.getEx());
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -188,7 +193,8 @@ public interface DependenceStrategy {
|
||||
* @param theseWrapper 该方法唯一有效参数。
|
||||
* @return 返回生成的 {@link DependenceAction.WithProperty)
|
||||
*/
|
||||
static DependenceStrategy theseWrapperAllSuccess(Set<WorkerWrapper<?,?>> theseWrapper) {
|
||||
@SuppressWarnings("unused")
|
||||
static DependenceStrategy theseWrapperAllSuccess(Set<WorkerWrapper<?, ?>> theseWrapper) {
|
||||
return new DependenceStrategy() {
|
||||
private final Set<WorkerWrapper<?, ?>> theseWrappers;
|
||||
private final String toString;
|
||||
@@ -199,7 +205,7 @@ public interface DependenceStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
boolean hasWaiting = false;
|
||||
@@ -232,7 +238,17 @@ public interface DependenceStrategy {
|
||||
};
|
||||
}
|
||||
|
||||
DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() {
|
||||
/**
|
||||
* 此值用于适配v1.4及之前的must开关模式,
|
||||
* 当`wrapperStrategy`的`dependMustStrategyMapper`的`mustDependSet`不为空时,
|
||||
* 则休息(因为能判断到这个责任链说明set中存在不满足的值L)。
|
||||
* 为空时,则任一成功则执行。
|
||||
*
|
||||
* @deprecated 不推荐使用must开关
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.jd.platform.async.wrapper.strategy.skip;
|
||||
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/6-下午3:02
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SkipStrategy {
|
||||
/**
|
||||
* 跳过策略函数。返回true将会使WorkerWrapper跳过执行。
|
||||
*
|
||||
* @param nextWrappers 下游WrapperSet
|
||||
* @param thisWrapper 本WorkerWrapper
|
||||
* @param fromWrapper 呼叫本Wrapper的上游Wrapper
|
||||
* @return 返回true将会使WorkerWrapper跳过执行。
|
||||
*/
|
||||
boolean shouldSkip(Set<WorkerWrapper<?, ?>> nextWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper);
|
||||
|
||||
/**
|
||||
* 不跳过
|
||||
*/
|
||||
SkipStrategy NOT_SKIP = new SkipStrategy() {
|
||||
@Override
|
||||
public boolean shouldSkip(Set<WorkerWrapper<?, ?>> 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<WorkerWrapper<?, ?>> 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";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker implements IWorker<String, User>, ICallback<String, User> {
|
||||
|
||||
@Override
|
||||
public User action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public User action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker1 implements IWorker<WorkResult<User>, User>, ICallback<WorkResult<User>, User> {
|
||||
|
||||
@Override
|
||||
public User action(WorkResult<User> result, Map<String, WorkerWrapper> allWrappers) {
|
||||
public User action(WorkResult<User> result, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
System.out.println("par1的入参来自于par0: " + result.getResult());
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker2 implements IWorker<WorkResult<User>, String>, ICallback<WorkResult<User>, String> {
|
||||
|
||||
@Override
|
||||
public String action(WorkResult<User> result, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(WorkResult<User> result, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
System.out.println("par2的入参来自于par1: " + result.getResult());
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
@@ -10,10 +10,11 @@ import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
* @author sjsdfg
|
||||
* @since 2020/6/14
|
||||
*/
|
||||
@SuppressWarnings({"deprecation", "RedundantStringFormatCall"})
|
||||
class LambdaTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
WorkerWrapper<WorkResult<User>, String> workerWrapper2 = new WorkerWrapper.Builder<WorkResult<User>, String>()
|
||||
.worker((WorkResult<User> result, Map<String, WorkerWrapper> allWrappers) -> {
|
||||
.worker((WorkResult<User> result, Map<String, WorkerWrapper<?, ?>> allWrappers) -> {
|
||||
System.out.println("par2的入参来自于par1: " + result.getResult());
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
@@ -28,7 +29,7 @@ class LambdaTest {
|
||||
.build();
|
||||
|
||||
WorkerWrapper<WorkResult<User>, User> workerWrapper1 = new WorkerWrapper.Builder<WorkResult<User>, User>()
|
||||
.worker((WorkResult<User> result, Map<String, WorkerWrapper> allWrappers) -> {
|
||||
.worker((WorkResult<User> result, Map<String, WorkerWrapper<?, ?>> allWrappers) -> {
|
||||
System.out.println("par1的入参来自于par0: " + result.getResult());
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
@@ -44,7 +45,7 @@ class LambdaTest {
|
||||
.build();
|
||||
|
||||
WorkerWrapper<String, User> workerWrapper = new WorkerWrapper.Builder<String, User>()
|
||||
.worker((String object, Map<String, WorkerWrapper> allWrappers) -> {
|
||||
.worker((String object, Map<String, WorkerWrapper<?,?>> allWrappers) -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker implements IWorker<String, User>, ICallback<String, User> {
|
||||
|
||||
@Override
|
||||
public User action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public User action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker1 implements IWorker<String, User>, ICallback<String, User> {
|
||||
|
||||
@Override
|
||||
public User action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public User action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
System.out.println("-----------------");
|
||||
System.out.println("获取par0的执行结果: " + allWrappers.get("first").getWorkResult());
|
||||
System.out.println("取par0的结果作为自己的入参,并将par0的结果加上一些东西");
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
class DeWorker2 implements IWorker<User, String>, ICallback<User, String> {
|
||||
|
||||
@Override
|
||||
public String action(User object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(User object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
System.out.println("-----------------");
|
||||
System.out.println("par1的执行结果是: " + allWrappers.get("second").getWorkResult());
|
||||
System.out.println("取par1的结果作为自己的入参,并将par1的结果加上一些东西");
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class ParTimeoutWorker implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class ParWorker implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -20,7 +20,7 @@ class ParWorker1 implements IWorker<String, String>, ICallback<String, String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -20,7 +20,7 @@ class ParWorker2 implements IWorker<String, String>, ICallback<String, String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -19,7 +19,7 @@ class ParWorker3 implements IWorker<String, String>, ICallback<String, String> {
|
||||
this.sleepTime = sleepTime;
|
||||
}
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class ParWorker4 implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class SeqTimeoutWorker implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class SeqWorker implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class SeqWorker1 implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
||||
class SeqWorker2 implements IWorker<String, String>, ICallback<String, String> {
|
||||
|
||||
@Override
|
||||
public String action(String object, Map<String, WorkerWrapper> allWrappers) {
|
||||
public String action(String object, Map<String, WorkerWrapper<?,?>> allWrappers) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
37
asyncTool-core/src/test/java/v15/cases/Case0.java
Normal file
37
asyncTool-core/src/test/java/v15/cases/Case0.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:10
|
||||
*/
|
||||
class Case0 {
|
||||
static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
WorkerWrapper<?, ?> a = builder("A").build();
|
||||
WorkerWrapper<?, ?> b = builder("B").build();
|
||||
WorkerWrapper<?, ?> c = builder("C").build();
|
||||
try {
|
||||
Async.work(100, a, b, c).awaitFinish();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=C) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
37
asyncTool-core/src/test/java/v15/cases/Case01.java
Normal file
37
asyncTool-core/src/test/java/v15/cases/Case01.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
|
||||
/**
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:13
|
||||
*/
|
||||
class Case01 {
|
||||
static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
WorkerWrapper<?, ?> a = builder("A").build();
|
||||
WorkerWrapper<?, ?> b = builder("B").depends(a).build();
|
||||
WorkerWrapper<?, ?> c = builder("C").depends(a).build();
|
||||
WorkerWrapper<?, ?> f = builder("F").depends(b, c).build();
|
||||
try {
|
||||
Async.work(100, a).awaitFinish();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=C) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=F) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
38
asyncTool-core/src/test/java/v15/cases/Case02.java
Normal file
38
asyncTool-core/src/test/java/v15/cases/Case02.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
|
||||
/**
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/25-下午9:15
|
||||
*/
|
||||
class Case02 {
|
||||
static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
WorkerWrapper<?, ?> f = builder("F").build();
|
||||
WorkerWrapper<?, ?> a = builder("A")
|
||||
.nextOf(builder("B").nextOf(f).build())
|
||||
.nextOf(builder("C").nextOf(f).build())
|
||||
.build();
|
||||
try {
|
||||
Async.work(100, a).awaitFinish();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=C) is working
|
||||
wrapper(id=F) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
62
asyncTool-core/src/test/java/v15/cases/Case1.java
Normal file
62
asyncTool-core/src/test/java/v15/cases/Case1.java
Normal file
@@ -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.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
WorkerWrapper<?, ?> a = builder("A").build();
|
||||
WorkerWrapper<?, ?> d;
|
||||
builder("H")
|
||||
.depends(
|
||||
builder("F")
|
||||
.depends(builder("B").depends(a).build())
|
||||
.depends(builder("C").depends(a).build())
|
||||
.build(),
|
||||
builder("G")
|
||||
.depends(builder("E")
|
||||
.depends(d = builder("D").build())
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
try {
|
||||
Async.work(1000, a, d).awaitFinish();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=D) is working
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=E) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=C) is working
|
||||
wrapper(id=G) is working
|
||||
wrapper(id=F) is working
|
||||
wrapper(id=H) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
72
asyncTool-core/src/test/java/v15/cases/Case10.java
Normal file
72
asyncTool-core/src/test/java/v15/cases/Case10.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.worker.OnceWork;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* @author tcsnzh[zh.jobs@foxmail.com] create this in 2021/5/26-下午4:07
|
||||
*/
|
||||
class Case10 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return builder(id, -1L);
|
||||
}
|
||||
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id, long sleepTime) {
|
||||
return WorkerWrapper.<String, String>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<String, String>() {
|
||||
@Override
|
||||
public void begin() {
|
||||
System.out.println("wrapper(id=" + id + ") has begin . ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void result(boolean success, String param, WorkResult<String> workResult) {
|
||||
System.out.println("\t\twrapper(id=" + id + ") callback "
|
||||
+ (success ? "success " : "fail ")
|
||||
+ ", workResult is " + workResult);
|
||||
}
|
||||
}))
|
||||
.allowInterrupt(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A(10ms) ==> B(10ms) ==> C(10ms)
|
||||
*/
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
final WorkerWrapper<?, ?> c;
|
||||
final WorkerWrapper<?, ?> b;
|
||||
final WorkerWrapper<?, ?> a = builder("A", 10)
|
||||
.nextOf(b = builder("B", 10)
|
||||
.nextOf(c = builder("C", 10).build())
|
||||
.build())
|
||||
.build();
|
||||
final OnceWork onceWork = Async.work(40, a);
|
||||
Thread.sleep(25);
|
||||
onceWork.pleaseCancelAndAwaitFinish();
|
||||
System.out.println("任务b信息 " + b);
|
||||
System.out.println("任务c信息 " + c);
|
||||
System.out.println("OnceWork信息 " + onceWork);
|
||||
/*
|
||||
可以看到C的state为SKIP,workResult.ex为CancelSkippedException,即被取消了。
|
||||
不过有时程序运行慢,导致B被取消了,那么C就不会执行,其状态就为INIT了。
|
||||
*/
|
||||
}
|
||||
}
|
||||
53
asyncTool-core/src/test/java/v15/cases/Case2.java
Normal file
53
asyncTool-core/src/test/java/v15/cases/Case2.java
Normal file
@@ -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<Integer, Integer> {
|
||||
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<String, WorkerWrapper<?,?>> 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<Integer, Integer> wrapper100 = WorkerWrapper.<Integer, Integer>builder()
|
||||
.id("id:100").worker(new AddWork()).param(100).build();
|
||||
WorkerWrapper<Integer, Integer> wrapper200 = WorkerWrapper.<Integer, Integer>builder()
|
||||
.id("id:200").worker(new AddWork()).param(200).build();
|
||||
WorkerWrapper<Integer, Integer> add = WorkerWrapper.<Integer, Integer>builder().id("id:add")
|
||||
.worker(new AddWork("id:100", "id:200")).depends(wrapper100, wrapper200).build();
|
||||
Async.work(20,wrapper100,wrapper200).awaitFinish();
|
||||
System.out.println(add.getWorkResult());
|
||||
// 输出WorkResult{result=300, resultState=SUCCESS, ex=null}
|
||||
}
|
||||
}
|
||||
63
asyncTool-core/src/test/java/v15/cases/Case3.java
Normal file
63
asyncTool-core/src/test/java/v15/cases/Case3.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 示例:设置依赖策略--快速上手
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/8-下午10:58
|
||||
*/
|
||||
class Case3 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
WorkerWrapper<?, ?> a = builder("A").build();
|
||||
WorkerWrapper<?, ?> b1 = builder("B1").depends(a).build();
|
||||
WorkerWrapper<?, ?> b2 = builder("B2").depends(a).build();
|
||||
WorkerWrapper<?, ?> b3 = builder("B3").depends(a).build();
|
||||
WorkerWrapper<?, ?> b4 = builder("B4").depends(a).build();
|
||||
WorkerWrapper<?, ?> b5 = builder("B5").depends(a).build();
|
||||
WorkerWrapper<?, ?> c1 = builder("C1")
|
||||
.depends(DependenceStrategy.ALL_DEPENDENCIES_ALL_SUCCESS, b1, b2)
|
||||
.build();
|
||||
WorkerWrapper<?, ?> c2 = builder("C2")
|
||||
.depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS, b3, b4, b5)
|
||||
.build();
|
||||
// 这里用线程数较少的线程池做示例,对于ALL_DEPENDENCIES_ANY_SUCCESS“仅需一个”的效果会好一点
|
||||
ExecutorService pool = Executors.newFixedThreadPool(2);
|
||||
try {
|
||||
Async.work(1000, pool, a).awaitFinish();
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B3) is working
|
||||
wrapper(id=B1) is working
|
||||
wrapper(id=B2) is working
|
||||
wrapper(id=C2) is working
|
||||
wrapper(id=C1) is working
|
||||
wrapper(id=B4) is working
|
||||
// 我们看到B5被跳过了,没有执行callback
|
||||
*/
|
||||
}
|
||||
}
|
||||
69
asyncTool-core/src/test/java/v15/cases/Case4.java
Normal file
69
asyncTool-core/src/test/java/v15/cases/Case4.java
Normal file
@@ -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.<String, String>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<WorkerWrapper<?, ?>> dependWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper) {
|
||||
return dependWrappers.stream()
|
||||
.filter(workerWrapper -> workerWrapper.getWorkResult().getResultState() == ResultState.SUCCESS)
|
||||
.count() > 3 ?
|
||||
DependenceAction.START_WORK.emptyProperty()
|
||||
: DependenceAction.TAKE_REST.emptyProperty();
|
||||
}
|
||||
}).end()
|
||||
.build();
|
||||
for (int i = 1; i < 10; i++) {
|
||||
builder("B" + i).depends(a).nextOf(c).build();
|
||||
}
|
||||
ExecutorService pool = Executors.newFixedThreadPool(2);
|
||||
try {
|
||||
Async.work(1000, pool, a).awaitFinish();
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B2) is working
|
||||
wrapper(id=B1) is working
|
||||
wrapper(id=B4) is working
|
||||
wrapper(id=B3) is working
|
||||
wrapper(id=B5) is working
|
||||
wrapper(id=C) is working
|
||||
由于B1-B10是并行的,所以正好仅有3个wrapper成功,在多线程环境中是比较难遇到的。
|
||||
*/
|
||||
}
|
||||
}
|
||||
75
asyncTool-core/src/test/java/v15/cases/Case5.java
Normal file
75
asyncTool-core/src/test/java/v15/cases/Case5.java
Normal file
@@ -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.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
|
||||
WorkerWrapper<?, ?> a1 = builder("A1").build();
|
||||
WorkerWrapper<?, ?> a2 = builder("A2").build();
|
||||
WorkerWrapper<?, ?> a3 = builder("A3").build();
|
||||
WorkerWrapper<?, ?> a4 = builder("A4").build();
|
||||
WorkerWrapper<?, ?> a5 = builder("A5").build();
|
||||
WorkerWrapper<?, ?> a6 = builder("A6").build();
|
||||
WorkerWrapper<?, ?> a7 = builder("A7").build();
|
||||
WorkerWrapper<?, ?> a8 = builder("A8").build();
|
||||
WorkerWrapper<?, ?> a9 = builder("A9").build();
|
||||
WorkerWrapper<?, ?> a10 = builder("A10").build();
|
||||
builder("B")
|
||||
.setDepend()
|
||||
// 必须a3、a4成功才能执行
|
||||
.mustRequireWrapper(a3, a4)
|
||||
// 如果a3、a4没有成功,则休息
|
||||
.strategy((dependWrappers, thisWrapper, fromWrapper) -> DependenceAction.TAKE_REST.emptyProperty())
|
||||
.wrapper(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
.end()
|
||||
.build();
|
||||
WorkerWrapper<?, ?> start = builder("start").nextOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10).build();
|
||||
ExecutorService pool = Executors.newFixedThreadPool(2);
|
||||
try {
|
||||
Async.work(1000, pool, start).awaitFinish();
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A1) is working
|
||||
wrapper(id=A2) is working
|
||||
wrapper(id=A4) is working
|
||||
wrapper(id=A3) is working
|
||||
wrapper(id=A5) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=A6) is working
|
||||
我们可以看到,A3、A4执行后,B也执行了,之后的wrapper被跳过了
|
||||
(这里之所以a5、a6还在执行,只是因为他两正好在WORKING,所以没发现后面的B已经可以跳过了)
|
||||
*/
|
||||
}
|
||||
}
|
||||
59
asyncTool-core/src/test/java/v15/cases/Case6.java
Normal file
59
asyncTool-core/src/test/java/v15/cases/Case6.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceAction;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* 示例:自定义依赖策略--对单个wrapper设置“上克下”策略--简单使用与示例
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-上午1:42
|
||||
*/
|
||||
class Case6 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
//noinspection unchecked
|
||||
WorkerWrapper<?, ?> b = builder("B")
|
||||
// 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。
|
||||
.depends((dependWrappers, thisWrapper, fromWrapper) ->
|
||||
DependenceAction.FAST_FAIL
|
||||
.fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他"))
|
||||
)
|
||||
.callback(ICallback.PRINT_EXCEPTION_STACK_TRACE)
|
||||
.build();
|
||||
WorkerWrapper<?, ?> a = builder("A")
|
||||
.setNext()
|
||||
// a将会使b直接开始工作
|
||||
// 若是去掉这行代码,则b会失败
|
||||
.specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b)
|
||||
.wrapper(b)
|
||||
.end().build();
|
||||
Async.work(1000, a).awaitFinish();
|
||||
System.out.println(a.getWorkResult());
|
||||
System.out.println(b.getWorkResult());
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B) is working
|
||||
WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
*/
|
||||
}
|
||||
}
|
||||
61
asyncTool-core/src/test/java/v15/cases/Case7.java
Normal file
61
asyncTool-core/src/test/java/v15/cases/Case7.java
Normal file
@@ -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.<String, String>builder()
|
||||
.id(id)
|
||||
.worker((param, allWrappers) -> {
|
||||
System.out.println("wrapper(id=" + id + ") is working");
|
||||
if (sleepTime > 0) {
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A ==> B(10ms) ==> C ==> D (D可在E、C任意一个完成后执行)
|
||||
* . \====> E(5ms) ====/
|
||||
*/
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
WorkerWrapper<?, ?> d = builder("D").depends(DependenceStrategy.ALL_DEPENDENCIES_ANY_SUCCESS).build();
|
||||
WorkerWrapper<?, ?> a = builder("A")
|
||||
.nextOf(builder("B", 10)
|
||||
.nextOf(builder("C")
|
||||
.nextOf(d)
|
||||
// 这里我们没有设置C的跳过策略,因为默认使用CHECK_ONE_LEVEL,可将下行代码注释去掉,则C会执行
|
||||
// .setSkipStrategy(SkipStrategy.NOT_SKIP)
|
||||
.build())
|
||||
.build(),
|
||||
builder("E", 5).nextOf(d).build()
|
||||
).build();
|
||||
Async.work(1000, a).awaitFinish();
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=E) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=D) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
74
asyncTool-core/src/test/java/v15/cases/Case8.java
Normal file
74
asyncTool-core/src/test/java/v15/cases/Case8.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/9-下午4:34
|
||||
*/
|
||||
class Case8 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return builder(id, -1L);
|
||||
}
|
||||
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id, long sleepTime) {
|
||||
return WorkerWrapper.<String, String>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<String, String>() {
|
||||
@Override
|
||||
public void begin() {
|
||||
System.out.println("wrapper(id=" + id + ") has begin . ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void result(boolean success, String param, WorkResult<String> workResult) {
|
||||
System.out.println("\t\twrapper(id=" + id + ") callback "
|
||||
+ (success ? "success " : "fail ")
|
||||
+ ", workResult is " + workResult);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* A ==> B(10ms) ==> C(20ms)
|
||||
*/
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
WorkerWrapper<?, ?> a = builder("A")
|
||||
.nextOf(builder("B", 10)
|
||||
.nextOf(builder("C", 20).build())
|
||||
.build())
|
||||
.build();
|
||||
Async.work(20, a).awaitFinish();
|
||||
/* 输出:
|
||||
wrapper(id=A) has begin .
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=A) callback success , workResult is WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
wrapper(id=B) has begin .
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=B) callback success , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null}
|
||||
wrapper(id=C) has begin .
|
||||
wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null}
|
||||
java.lang.InterruptedException: sleep interrupted
|
||||
at java.lang.Thread.sleep(Native Method)
|
||||
...
|
||||
以下异常信息省略
|
||||
*/
|
||||
}
|
||||
}
|
||||
65
asyncTool-core/src/test/java/v15/cases/Case9.java
Normal file
65
asyncTool-core/src/test/java/v15/cases/Case9.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.callback.DefaultCallback;
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.openutil.collection.CommonDirectedGraph;
|
||||
import com.jd.platform.async.openutil.collection.DirectedGraph;
|
||||
import com.jd.platform.async.wrapper.QuickBuildWorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.strategy.WrapperStrategy;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 快速构造示例。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/17-下午5:23
|
||||
*/
|
||||
class Case9 {
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
DirectedGraph<WorkerWrapper<?, ?>, Object> graph = DirectedGraph.synchronizedDigraph(new CommonDirectedGraph<>());
|
||||
QuickBuildWorkerWrapper<Object, Object> w1 = new QuickBuildWorkerWrapper<>("id1",
|
||||
null,
|
||||
(object, allWrappers) -> {
|
||||
System.out.println("I am IWorker 1");
|
||||
return null;
|
||||
},
|
||||
new DefaultCallback<>(),
|
||||
false,
|
||||
true,
|
||||
100,
|
||||
TimeUnit.MILLISECONDS,
|
||||
new WrapperStrategy.DefaultWrapperStrategy(),
|
||||
graph
|
||||
);
|
||||
QuickBuildWorkerWrapper<Object, Object> w2 = new QuickBuildWorkerWrapper<>("id2",
|
||||
null,
|
||||
(object, allWrappers) -> {
|
||||
System.out.println("I am IWorker 2");
|
||||
return null;
|
||||
},
|
||||
new DefaultCallback<>(),
|
||||
false,
|
||||
true,
|
||||
100,
|
||||
TimeUnit.MILLISECONDS,
|
||||
new WrapperStrategy.DefaultWrapperStrategy(),
|
||||
graph
|
||||
);
|
||||
graph.addNode(w1, w2);
|
||||
graph.putRelation(w1, new Object(), w2);
|
||||
|
||||
// System.out.println(graph);
|
||||
|
||||
Async.work(200, w1).awaitFinish();
|
||||
|
||||
System.out.println(" Begin work end .\n w1 : " + w1 + "\n w2 : " + w2 + "\n");
|
||||
/* 输出:
|
||||
I am IWorker 1
|
||||
I am IWorker 2
|
||||
Begin work end .
|
||||
w1 : 省略
|
||||
w2 : 省略
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -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的情况:
|
||||
* <p>
|
||||
* 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);
|
||||
19
asyncTool-openutil/pom.xml
Normal file
19
asyncTool-openutil/pom.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>asyncTool</artifactId>
|
||||
<groupId>com.jd.platform</groupId>
|
||||
<version>1.5.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>asyncTool-openutil</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.jd.platform.async.openutil;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* 两个int值包装类。重写了{@link #hashCode()}与{@link #equals(Object)}
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/16-上午1:50
|
||||
*/
|
||||
public final class BiInt {
|
||||
// properties
|
||||
|
||||
private final int m;
|
||||
private final int n;
|
||||
|
||||
public static final Comparator<BiInt> cmp_m_asc = Comparator.comparingInt(BiInt::getM);
|
||||
public static final Comparator<BiInt> cmp_n_asc = Comparator.comparingInt(BiInt::getN);
|
||||
public static final Comparator<BiInt> cmp_m_desc = cmp_m_asc.reversed();
|
||||
public static final Comparator<BiInt> cmp_n_desc = cmp_n_asc.reversed();
|
||||
public static final Comparator<BiInt> cmp_m_asc_n_asc =
|
||||
cmp_m_asc.thenComparing(cmp_n_asc);
|
||||
public static final Comparator<BiInt> cmp_m_asc_n_desc =
|
||||
cmp_m_asc.thenComparing(cmp_n_desc);
|
||||
public static final Comparator<BiInt> cmp_m_desc_n_asc =
|
||||
cmp_m_desc.thenComparing(cmp_n_asc);
|
||||
public static final Comparator<BiInt> cmp_m_desc_n_desc =
|
||||
cmp_m_desc.thenComparing(cmp_n_desc);
|
||||
public static final Comparator<BiInt> cmp_n_asc_m_asc =
|
||||
cmp_n_asc.thenComparing(cmp_m_asc);
|
||||
public static final Comparator<BiInt> cmp_n_asc_m_desc =
|
||||
cmp_n_asc.thenComparing(cmp_m_desc);
|
||||
public static final Comparator<BiInt> cmp_n_desc_m_asc =
|
||||
cmp_n_desc.thenComparing(cmp_m_asc);
|
||||
public static final Comparator<BiInt> cmp_n_desc_m_desc =
|
||||
cmp_n_desc.thenComparing(cmp_m_desc);
|
||||
|
||||
/**
|
||||
* private constructor , please use {@link #of(int, int)} to build Idx object.
|
||||
*/
|
||||
private BiInt(int m, int n) {
|
||||
this.m = m;
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
// getter
|
||||
|
||||
public int getM() {
|
||||
return m;
|
||||
}
|
||||
|
||||
public int getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
// hashcode and equals
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return m ^ n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof BiInt))
|
||||
return false;
|
||||
BiInt idx = (BiInt) o;
|
||||
return m == idx.m && n == idx.n;
|
||||
}
|
||||
|
||||
// toString
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + m + ',' + n + ')';
|
||||
}
|
||||
|
||||
// ========== static ==========
|
||||
|
||||
|
||||
// 工厂方法
|
||||
|
||||
public static BiInt of(int m, int n) {
|
||||
if (m == Integer.MIN_VALUE && n == Integer.MAX_VALUE) {
|
||||
return MIN_TO_MAX;
|
||||
}
|
||||
if (m >= 0 && m < CACHE_RANGE_M && n >= 0 && n < CACHE_RANGE_M) {
|
||||
return cache[m * CACHE_RANGE_M + n];
|
||||
}
|
||||
return new BiInt(m, n);
|
||||
}
|
||||
|
||||
// 缓存区间
|
||||
|
||||
private static final BiInt MIN_TO_MAX = new BiInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
private static final BiInt[] cache; // m from 0 to 31 , n from 0 to 31 , total 1023 .
|
||||
private static final int CACHE_RANGE_M = 32; // 0 to 31
|
||||
private static final int CACHE_RANGE_N = 32; // 0 to 31
|
||||
|
||||
static {
|
||||
cache = new BiInt[CACHE_RANGE_M * CACHE_RANGE_N];
|
||||
for (int i = 0; i < CACHE_RANGE_M; i++) {
|
||||
for (int j = 0; j < CACHE_RANGE_N; j++) {
|
||||
cache[i * CACHE_RANGE_M + j] = new BiInt(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import com.jd.platform.async.openutil.BiInt;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/14-下午9:51
|
||||
*/
|
||||
public abstract class AbstractArray2D<E> implements Array2D<E> {
|
||||
/**
|
||||
* 用于代替null
|
||||
*/
|
||||
protected static final Object NULL = new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsDoesntCheckParameterClass")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
//noinspection ConstantConditions
|
||||
return obj == null || obj == NULL || obj.equals(null);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(64).append(this.getClass().getSimpleName()).append('{');
|
||||
Iterator<Point<E>> it = iterator();
|
||||
if (it.hasNext()) {
|
||||
while (true) {
|
||||
Point<E> point = it.next();
|
||||
sb.append('{').append(point.getIdx()).append(':').append(point.getElement()).append('}');
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
return sb.append('}').toString();
|
||||
}
|
||||
|
||||
public static class PointImpl<E> implements Point<E> {
|
||||
private final BiInt idx;
|
||||
private final E element;
|
||||
|
||||
public PointImpl(BiInt idx, E element) {
|
||||
this.idx = idx;
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiInt getIdx() {
|
||||
return idx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + idx + ":" + element + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 抽象有向图
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/13-上午11:37
|
||||
*/
|
||||
public abstract class AbstractDirectedGraph<N, R> implements DirectedGraph<N, R> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Set<N> nv = nodesView();
|
||||
Set<? extends Entry<N, R>> rSet = getRelations();
|
||||
StringBuilder sb = new StringBuilder(nv.size() * 10 + rSet.size() * 20)
|
||||
.append(this.getClass().getSimpleName()).append("{nodes=[");
|
||||
Iterator<N> nit = nodesView().iterator();
|
||||
if (nit.hasNext()) {
|
||||
for (; ; ) {
|
||||
sb.append(nit.next());
|
||||
if (!nit.hasNext()) {
|
||||
break;
|
||||
}
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append("], relations=[");
|
||||
Iterator<? extends Entry<N, R>> eit = rSet.iterator();
|
||||
if (eit.hasNext()) {
|
||||
for (; ; ) {
|
||||
sb.append(eit.next());
|
||||
if (!eit.hasNext()) {
|
||||
break;
|
||||
}
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
return sb.append("]}").toString();
|
||||
}
|
||||
|
||||
public abstract class AbstractNodesView extends AbstractSet<N> {
|
||||
@Override
|
||||
public boolean add(N n) {
|
||||
return AbstractDirectedGraph.this.addNode(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
N o1;
|
||||
//noinspection unchecked
|
||||
if (!AbstractDirectedGraph.this.containsNode(o1 = (N) o)) {
|
||||
return false;
|
||||
}
|
||||
AbstractDirectedGraph.this.removeNode(o1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class AbstractEntry<N, R> implements Entry<N, R> {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.getFrom().hashCode() ^ this.getTo().hashCode() ^ this.getRelation().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof Graph.Entry)) {
|
||||
return false;
|
||||
}
|
||||
Entry obj1 = (Entry) obj;
|
||||
return Objects.equals(this.getFrom(), obj1.getFrom())
|
||||
&& Objects.equals(this.getTo(), obj1.getTo())
|
||||
&& Objects.equals(this.getRelation(), obj1.getRelation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{from=" + getFrom() + ", relation=" + getRelation() + ", to=" + getTo() + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/14-上午2:33
|
||||
*/
|
||||
public abstract class AbstractStoreArk<E> implements StoreArk<E> {
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(size() * 10).append(this.getClass().getSimpleName()).append("{");
|
||||
if (!isEmpty()) {
|
||||
stream().forEach(entry -> sb.append(entry.getKey()).append(":").append(entry.getValue()).append(", "));
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
return sb.append("}").toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import com.jd.platform.async.openutil.BiInt;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* 二维数组
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/14-下午9:50
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface Array2D<E> extends Iterable<Array2D.Point<E>> {
|
||||
/**
|
||||
* 有多少行
|
||||
*/
|
||||
int lineLength();
|
||||
|
||||
/**
|
||||
* 有多少列
|
||||
*/
|
||||
int columnLength();
|
||||
|
||||
/**
|
||||
* 添加元素到指定位置
|
||||
*
|
||||
* @param line 行
|
||||
* @param column 列
|
||||
* @param element 元素
|
||||
* @return 如果之前添加过元素,将返回替换掉的之前的元素
|
||||
* @throws IndexOutOfBoundsException 行列超出范围
|
||||
*/
|
||||
E add(int line, int column, E element);
|
||||
|
||||
/**
|
||||
* 如果不存在的话则添加元素
|
||||
* <p>
|
||||
* {@link #add(int, int, Object)}
|
||||
*
|
||||
* @return 不存在且成功添加,返回true。
|
||||
*/
|
||||
default boolean addIfAbsent(int line, int column, E element) {
|
||||
if (get(line, column) != null) {
|
||||
return false;
|
||||
}
|
||||
add(line, column, element);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除元素
|
||||
*
|
||||
* @param line 行
|
||||
* @param column 列
|
||||
* @return 返回移出的元素
|
||||
* @throws IndexOutOfBoundsException 行列超出返回
|
||||
* @throws IllegalArgumentException 如果原本不存在元素
|
||||
*/
|
||||
E remove(int line, int column);
|
||||
|
||||
/**
|
||||
* 存在则移除,不存在则返回null
|
||||
*
|
||||
* @param line 行
|
||||
* @param column 列
|
||||
* @return 如果不存在,返回null。存在则返回被移出的元素。
|
||||
* @throws IndexOutOfBoundsException 行列超出范围
|
||||
*/
|
||||
default E removeIfAbsent(int line, int column) {
|
||||
if (get(line, column) == null) {
|
||||
return null;
|
||||
}
|
||||
return remove(line, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取元素
|
||||
*
|
||||
* @param line 行
|
||||
* @param column 列
|
||||
* @return 如果存在,返回该元素。不存在则返回null。
|
||||
* @throws IndexOutOfBoundsException 行列超出范围
|
||||
*/
|
||||
E get(int line, int column);
|
||||
|
||||
/**
|
||||
* 是否包含元素
|
||||
*
|
||||
* @param element 元素
|
||||
* @return 有这个元素就返回true。
|
||||
*/
|
||||
boolean containsElement(E element);
|
||||
|
||||
/**
|
||||
* 获取整行的元素
|
||||
*
|
||||
* @param line 行号
|
||||
* @return 返回key为列号,value为元素的Map
|
||||
* @throws IndexOutOfBoundsException 行号超出范围
|
||||
*/
|
||||
Map<Integer, E> fullLine(int line);
|
||||
|
||||
/**
|
||||
* 获取整列的元素
|
||||
*
|
||||
* @param column 列号
|
||||
* @return 返回key为行号,value为元素的Map
|
||||
* @throws IndexOutOfBoundsException 列号超出范围
|
||||
*/
|
||||
Map<Integer, E> fullColumn(int column);
|
||||
|
||||
/**
|
||||
* 迭代器
|
||||
*
|
||||
* @param foreachOrder 遍历顺序
|
||||
* @return 如果本容器不允许null值存在,只需返回存在的元素的键即可。如果允许null值存在,仅需返回包括人工放入的null值的键即可。
|
||||
*/
|
||||
Iterator<? extends Point<E>> iterator(Comparator<BiInt> foreachOrder);
|
||||
|
||||
@Override
|
||||
default Iterator<Point<E>> iterator() {
|
||||
//noinspection unchecked
|
||||
return (Iterator) iterator(BiInt.cmp_m_asc_n_asc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 流
|
||||
*/
|
||||
default Stream<? extends Point<E>> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
|
||||
default Stream<? extends Point<E>> parallelStream() {
|
||||
return StreamSupport.stream(spliterator(), true);
|
||||
}
|
||||
|
||||
default Spliterator<Point<E>> spliterator(Comparator<BiInt> foreachOrder) {
|
||||
return Spliterators.spliteratorUnknownSize(iterator(foreachOrder), 0);
|
||||
}
|
||||
|
||||
default Stream<? extends Point<E>> stream(Comparator<BiInt> foreachOrder) {
|
||||
return StreamSupport.stream(spliterator(foreachOrder), false);
|
||||
}
|
||||
|
||||
default Stream<? extends Point<E>> parallelStream(Comparator<BiInt> foreachOrder) {
|
||||
return StreamSupport.stream(spliterator(foreachOrder), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 端点
|
||||
*
|
||||
* @param <E> 元素泛型
|
||||
*/
|
||||
interface Point<E> {
|
||||
BiInt getIdx();
|
||||
|
||||
default int getLine() {
|
||||
return getIdx().getM();
|
||||
}
|
||||
|
||||
default int getColumn() {
|
||||
return getIdx().getN();
|
||||
}
|
||||
|
||||
E getElement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 一个缓存元素位置的储存柜。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/14-上午2:37
|
||||
*/
|
||||
public class CachedStoreArk<E> extends AbstractStoreArk<E> {
|
||||
private final StoreArk<E> inner;
|
||||
|
||||
private final Map<E, Integer> cacheMap = new HashMap<>();
|
||||
|
||||
public CachedStoreArk() {
|
||||
this(CommonStoreArk::new);
|
||||
}
|
||||
|
||||
private CachedStoreArk(Supplier<StoreArk<E>> sup) {
|
||||
this.inner = sup.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int store(E element) {
|
||||
int id = inner.store(element);
|
||||
cacheMap.put(element, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek(int id) {
|
||||
return inner.peek(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E takeOut(int id) {
|
||||
E e = inner.takeOut(id);
|
||||
cacheMap.remove(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return inner.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<Integer, E>> iterator() {
|
||||
return inner.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findId(E element) {
|
||||
Integer idNullable = cacheMap.get(element);
|
||||
return idNullable == null ? -1 : idNullable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 线程不安全的有向图。
|
||||
* <p/>
|
||||
* 不允许放入null。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/14-上午2:22
|
||||
*/
|
||||
public class CommonDirectedGraph<N, R> extends AbstractDirectedGraph<N, R> {
|
||||
|
||||
// ========== properties ==========
|
||||
|
||||
private final StoreArk<N> nodes = new CachedStoreArk<>();
|
||||
private final Array2D<R> arr = new SparseArray2D<>();
|
||||
|
||||
// ========== methods ==========
|
||||
|
||||
@Override
|
||||
public boolean addNode(N node) {
|
||||
if (containsNode(Objects.requireNonNull(node))) {
|
||||
return false;
|
||||
}
|
||||
nodes.store(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsNode(N node) {
|
||||
return node != null && findNodeId(node, false) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> removeNode(N node) {
|
||||
int id = findNodeId(Objects.requireNonNull(node), true);
|
||||
LinkedHashSet<Entry<N, R>> res = new LinkedHashSet<>();
|
||||
// 查找node为from的键
|
||||
arr.fullLine(id).forEach((toNodeId, relation) -> {
|
||||
res.add(new OuterEntry<>(node, nodes.peek(toNodeId), relation));
|
||||
arr.remove(id, toNodeId);
|
||||
});
|
||||
// 查找node为to的键
|
||||
arr.fullColumn(id).forEach((fromNodeId, relation) -> {
|
||||
// 在上一次遍历中,fromNodeId为id,
|
||||
if (fromNodeId == id) {
|
||||
return;
|
||||
}
|
||||
res.add(new OuterEntry<>(nodes.peek(fromNodeId), node, relation));
|
||||
arr.remove(fromNodeId, id);
|
||||
});
|
||||
nodes.takeOut(id);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R putRelation(N fromNode, R relation, N toNode) {
|
||||
return arr.add(
|
||||
findNodeId(Objects.requireNonNull(fromNode), true),
|
||||
findNodeId(Objects.requireNonNull(toNode), true),
|
||||
Objects.requireNonNull(relation)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationFrom(N from) {
|
||||
int id = findNodeId(Objects.requireNonNull(from), true);
|
||||
LinkedHashSet<Entry<N, R>> res = new LinkedHashSet<>();
|
||||
// 查找node为from的键
|
||||
arr.fullLine(id).forEach((toNodeId, relation) -> res.add(new OuterEntry<>(from, nodes.peek(toNodeId), relation)));
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationTo(N to) {
|
||||
int id = findNodeId(Objects.requireNonNull(to), true);
|
||||
LinkedHashSet<Entry<N, R>> res = new LinkedHashSet<>();
|
||||
// 查找node为to的键
|
||||
arr.fullColumn(id).forEach((fromNodeId, relation) ->
|
||||
res.add(new OuterEntry<>(nodes.peek(fromNodeId), to, relation)));
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<N> nodesView() {
|
||||
return new AbstractNodesView() {
|
||||
@Override
|
||||
public Iterator<N> iterator() {
|
||||
return nodes.stream().map(Map.Entry::getValue).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return nodes.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelations() {
|
||||
return arr.stream().map((Function<Array2D.Point<R>, Entry<N, R>>) rPoint -> new OuterEntry<>(
|
||||
nodes.peek(rPoint.getLine()),
|
||||
nodes.peek(rPoint.getColumn()),
|
||||
rPoint.getElement()
|
||||
)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private int findNodeId(N node, boolean mustExistElseThrowEx) {
|
||||
int id = nodes.findId(Objects.requireNonNull(node));
|
||||
if (mustExistElseThrowEx && id < 0) {
|
||||
throw new IllegalArgumentException("No node exists : " + node);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static class OuterEntry<N, R> extends AbstractEntry<N, R> {
|
||||
private final N from;
|
||||
private final N to;
|
||||
private final R relation;
|
||||
|
||||
public OuterEntry(N from, N to, R relation) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.relation = relation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public N getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public N getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R getRelation() {
|
||||
return relation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 自动扩容的id储物柜,线程不安全。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/13-下午1:24
|
||||
*/
|
||||
public class CommonStoreArk<E> extends AbstractStoreArk<E> {
|
||||
private Object[] elements;
|
||||
|
||||
/**
|
||||
* 已经分配的下标数
|
||||
*/
|
||||
private int allocSize = 0;
|
||||
|
||||
/**
|
||||
* 保存着最小空元素的队列
|
||||
*/
|
||||
private final Queue<Integer> emptyPoints = new PriorityQueue<>(Integer::compareTo);
|
||||
|
||||
public CommonStoreArk(int initialCapacity) {
|
||||
elements = new Object[initialCapacity];
|
||||
}
|
||||
|
||||
public CommonStoreArk() {
|
||||
this(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int store(E element) {
|
||||
int id;
|
||||
elements[id = pollId()] = element;
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek(int id) {
|
||||
if (id < 0) {
|
||||
throw new IllegalArgumentException("id " + id + " can't be negative");
|
||||
}
|
||||
if (id >= elements.length) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (E) elements[id];
|
||||
}
|
||||
|
||||
@Override
|
||||
public E takeOut(int id) {
|
||||
if (id < 0) {
|
||||
throw new IllegalArgumentException("id " + id + " can't be negative");
|
||||
}
|
||||
if (id >= elements.length) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
E out = (E) elements[id];
|
||||
elements[id] = null;
|
||||
if (id == allocSize - 1) {
|
||||
allocSize--;
|
||||
} else {
|
||||
emptyPoints.add(id);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return allocSize - emptyPoints.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<Integer, E>> iterator() {
|
||||
return new Iterator<Map.Entry<Integer, E>>() {
|
||||
private final Map.Entry<Integer, E>[] items;
|
||||
|
||||
private int idx = 0;
|
||||
|
||||
{
|
||||
//noinspection unchecked
|
||||
items = new Map.Entry[size()];
|
||||
int itemsIdx = 0;
|
||||
Iterator<Integer> emptyPointItr = emptyPoints.iterator();
|
||||
for (int i = 0; i < allocSize; i++) {
|
||||
Object element = elements[i];
|
||||
if (element == null) {
|
||||
continue;
|
||||
}
|
||||
final int _i = i;
|
||||
//noinspection unchecked
|
||||
items[itemsIdx++] = new Map.Entry<Integer, E>() {
|
||||
private final int k = _i;
|
||||
private E v = (E) element;
|
||||
|
||||
@Override
|
||||
public Integer getKey() {
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getValue() {
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E setValue(E value) {
|
||||
E _v = this.v;
|
||||
this.v = value;
|
||||
return _v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + k + ':' + v + '}';
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return idx < items.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<Integer, E> next() {
|
||||
return items[idx++];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findId(E element) {
|
||||
int i = 0;
|
||||
for (Object o : elements) {
|
||||
if (Objects.equals(o, element)) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int pollId() {
|
||||
if (!emptyPoints.isEmpty()) {
|
||||
return emptyPoints.poll();
|
||||
}
|
||||
int id = allocSize++;
|
||||
int length = elements.length;
|
||||
if (id >= length) {
|
||||
// 扩容
|
||||
elements = Arrays.copyOf(elements, Math.max(length + 1, length + (length >> 1)));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/16-下午11:27
|
||||
*/
|
||||
public interface DirectedGraph<N, R> extends Graph<N, R> {
|
||||
@Override
|
||||
default boolean isDirected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static <N, R> DirectedGraph<N, R> readOnlyDigraph(DirectedGraph<N, R> source) {
|
||||
return new ReadOnlyDirectedGraph<>(source);
|
||||
}
|
||||
|
||||
static <N, R> DirectedGraph<N, R> synchronizedDigraph(DirectedGraph<N, R> source) {
|
||||
return synchronizedDigraph(source, new Object());
|
||||
}
|
||||
|
||||
static <N, R> DirectedGraph<N, R> synchronizedDigraph(DirectedGraph<N, R> source, Object mutex) {
|
||||
return new SyncDirectedGraph<>(source, mutex);
|
||||
}
|
||||
|
||||
class ReadOnlyDirectedGraph<N, R> extends AbstractDirectedGraph<N, R> {
|
||||
private final DirectedGraph<N, R> source;
|
||||
|
||||
public ReadOnlyDirectedGraph(DirectedGraph<N, R> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
private static UnsupportedOperationException readOnlyGraph() {
|
||||
return new UnsupportedOperationException("readOnly graph");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addNode(N node) {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsNode(N node) {
|
||||
return source.containsNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> removeNode(N node) {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R putRelation(N fromNode, R relation, N toNode) {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationFrom(N from) {
|
||||
return source.getRelationFrom(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationTo(N to) {
|
||||
return source.getRelationTo(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<N> nodesView() {
|
||||
return new AbstractSet<N>() {
|
||||
private final Set<N> nodesViewSource = source.nodesView();
|
||||
|
||||
@Override
|
||||
public Iterator<N> iterator() {
|
||||
return new Iterator<N>() {
|
||||
private final Iterator<N> iteratorSource = nodesViewSource.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iteratorSource.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public N next() {
|
||||
return iteratorSource.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return nodesViewSource.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(N n) {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw readOnlyGraph();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelations() {
|
||||
return source.getRelations();
|
||||
}
|
||||
}
|
||||
|
||||
class SyncDirectedGraph<N, R> extends AbstractDirectedGraph<N, R> {
|
||||
private final DirectedGraph<N, R> source;
|
||||
private final Object mutex;
|
||||
|
||||
public SyncDirectedGraph(DirectedGraph<N, R> source, Object mutex) {
|
||||
this.source = source;
|
||||
this.mutex = mutex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addNode(N node) {
|
||||
synchronized (mutex) {
|
||||
return source.addNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsNode(N node) {
|
||||
synchronized (mutex) {
|
||||
return source.containsNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> removeNode(N node) {
|
||||
synchronized (mutex) {
|
||||
return source.removeNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R putRelation(N fromNode, R relation, N toNode) {
|
||||
synchronized (mutex) {
|
||||
return source.putRelation(fromNode, relation, toNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationFrom(N from) {
|
||||
synchronized (mutex) {
|
||||
return source.getRelationFrom(from);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelationTo(N to) {
|
||||
synchronized (mutex) {
|
||||
return source.getRelationTo(to);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<N> nodesView() {
|
||||
synchronized (mutex) {
|
||||
return Collections.synchronizedSet(source.nodesView());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Entry<N, R>> getRelations() {
|
||||
synchronized (mutex) {
|
||||
return source.getRelations();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 图数据结构
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/13-上午11:37
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface Graph<N, R> {
|
||||
/**
|
||||
* 添加节点。
|
||||
* 如果节点已经存在,则不会添加。
|
||||
*
|
||||
* @param node 添加进图的节点
|
||||
* @return 添加成功返回true,如果节点已经存在返回false。
|
||||
*/
|
||||
boolean addNode(N node);
|
||||
|
||||
/**
|
||||
* 添加一堆Node,任一成功返回true
|
||||
*/
|
||||
default boolean addNode(N... nodes) {
|
||||
boolean success = false;
|
||||
for (N node : nodes) {
|
||||
if (addNode(node)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在节点
|
||||
*
|
||||
* @param node 节点。
|
||||
* @return 存在返回true,否则返回false。
|
||||
*/
|
||||
boolean containsNode(N node);
|
||||
|
||||
/**
|
||||
* 移除节点。
|
||||
* 返回与该节点有关系的,被一并移出的键。
|
||||
*
|
||||
* @param node 节点
|
||||
* @return 返回值不会为null。
|
||||
* @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出异常。
|
||||
*/
|
||||
Set<? extends Entry<N, R>> removeNode(N node);
|
||||
|
||||
/**
|
||||
* 添加关系
|
||||
* 在无向图中fromNode与toNode参数的位置调换没有影响。
|
||||
*
|
||||
* @param fromNode 从这个节点开始
|
||||
* @param relation 关系
|
||||
* @param toNode 以那个节点为目标
|
||||
* @return 如果之前存在关系,则会替换之前的关系,返回出被替换的之前存在的关系。如果之前没有关系,返回null。
|
||||
* @throws IllegalArgumentException 如果两个节点任一不存在本图中,抛出该异常。
|
||||
*/
|
||||
R putRelation(N fromNode, R relation, N toNode);
|
||||
|
||||
/**
|
||||
* 获取“从这个节点开始”的所有关系
|
||||
*
|
||||
* @param from 关系开始的节点
|
||||
* @return 返回 {@link Entry}键。
|
||||
*/
|
||||
Set<? extends Entry<N, R>> getRelationFrom(N from);
|
||||
|
||||
/**
|
||||
* 获取“以这个节点为目标”的所有关系
|
||||
*
|
||||
* @param to 被关系的节点
|
||||
* @return 返回 {@link Entry}键。
|
||||
*/
|
||||
Set<? extends Entry<N, R>> getRelationTo(N to);
|
||||
|
||||
/**
|
||||
* 返回全部节点视图
|
||||
*
|
||||
* @return 视图
|
||||
*/
|
||||
Set<N> nodesView();
|
||||
|
||||
/**
|
||||
* 返回全部关系,返回的是新Set
|
||||
*
|
||||
* @return 与本类无关的Set
|
||||
*/
|
||||
Set<? extends Entry<N, R>> getRelations();
|
||||
|
||||
/**
|
||||
* 是否有向
|
||||
*/
|
||||
boolean isDirected();
|
||||
|
||||
interface Entry<N, R> {
|
||||
N getFrom();
|
||||
|
||||
N getTo();
|
||||
|
||||
R getRelation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import com.jd.platform.async.openutil.BiInt;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 稀疏二维数组。
|
||||
* <p/>
|
||||
* 可以设置是否允许存入null。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/14-下午9:45
|
||||
*/
|
||||
public class SparseArray2D<E> extends AbstractArray2D<E> {
|
||||
|
||||
// ========== properties ==========
|
||||
|
||||
/**
|
||||
* 限制长宽,默认为Integer.MAX_VALUE。稀疏数组不在乎这些。
|
||||
*/
|
||||
private final int maxLineLength;
|
||||
private final int maxColumnLength;
|
||||
private final boolean allowNull;
|
||||
|
||||
private final Map<BiInt, Object> items = new HashMap<>();
|
||||
|
||||
// ========== index cache properties ==========
|
||||
|
||||
/**
|
||||
* 缓存行列索引
|
||||
*/
|
||||
private final NavigableMap<Integer, NavigableSet<Integer>> indexOfLine2columns = new TreeMap<>(Integer::compareTo);
|
||||
private final NavigableMap<Integer, NavigableSet<Integer>> indexOfColumn2lines = new TreeMap<>(Integer::compareTo);
|
||||
|
||||
// ========== constructor ==========
|
||||
|
||||
public SparseArray2D() {
|
||||
this(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public SparseArray2D(boolean allowNull) {
|
||||
this(Integer.MAX_VALUE, Integer.MAX_VALUE, allowNull);
|
||||
}
|
||||
|
||||
public SparseArray2D(int maxLineCapacity, int maxColumnCapacity) {
|
||||
this(maxLineCapacity, maxColumnCapacity, false);
|
||||
}
|
||||
|
||||
public SparseArray2D(int maxLineCapacity, int maxColumnCapacity, boolean allowNull) {
|
||||
this.maxLineLength = maxLineCapacity;
|
||||
this.maxColumnLength = maxColumnCapacity;
|
||||
this.allowNull = allowNull;
|
||||
}
|
||||
|
||||
// ========== public methods ==========
|
||||
@Override
|
||||
public int lineLength() {
|
||||
return maxLineLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int columnLength() {
|
||||
return maxColumnLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E add(int line, int column, E element) {
|
||||
if (!allowNull && element == null) {
|
||||
throw new NullPointerException("null is not allowed");
|
||||
}
|
||||
Object put = items.put(BiInt.of(checkLine(line), checkColumn(column)), element == null ? NULL : element);
|
||||
addIndex(line, column);
|
||||
//noinspection unchecked
|
||||
return NULL.equals(put) ? null : (E) put;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int line, int column) {
|
||||
BiInt idx = BiInt.of(checkLine(line), checkColumn(column));
|
||||
Object get = items.get(idx);
|
||||
if (get == null) {
|
||||
throw new IllegalArgumentException("There is no element in line " + line + " column " + column);
|
||||
}
|
||||
items.remove(idx);
|
||||
removeIndex(line, column);
|
||||
//noinspection unchecked
|
||||
return NULL.equals(get) ? null : (E) get;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该方法如果返回null,则分不清 之前存入了null 还是 没有存入过
|
||||
* <p>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public E get(int line, int column) {
|
||||
Object get = items.get(BiInt.of(checkLine(line), checkColumn(column)));
|
||||
//noinspection unchecked
|
||||
return NULL.equals(get) ? null : (E) get;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsElement(E element) {
|
||||
if (NULL.equals(element)) {
|
||||
if (!allowNull) {
|
||||
return false;
|
||||
}
|
||||
return items.values().stream().anyMatch(v -> NULL.equals(element));
|
||||
}
|
||||
return items.values().stream().anyMatch(element::equals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, E> fullLine(int line) {
|
||||
return Optional.ofNullable(indexOfLine2columns.get(line))
|
||||
.map(set -> set.stream()
|
||||
.collect(Collectors.toMap(column -> column, column -> {
|
||||
//noinspection unchecked
|
||||
return (E) items.get(BiInt.of(line, column));
|
||||
})))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, E> fullColumn(int column) {
|
||||
return Optional.ofNullable(indexOfColumn2lines.get(column))
|
||||
.map(set -> set.stream()
|
||||
.collect(Collectors.toMap(line -> line, line -> {
|
||||
//noinspection unchecked
|
||||
return (E) items.get(BiInt.of(line, column));
|
||||
})))
|
||||
.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<? extends Point<E>> iterator(Comparator<BiInt> foreachOrder) {
|
||||
return new Iterator<Point<E>>() {
|
||||
private final Iterator<Map.Entry<BiInt, Object>> it;
|
||||
private Point<E> last = null;
|
||||
private boolean removed = false;
|
||||
|
||||
{
|
||||
it = items.entrySet().stream()
|
||||
.sorted((o1, o2) -> foreachOrder.compare(o1.getKey(), o2.getKey()))
|
||||
.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point<E> next() {
|
||||
Map.Entry<BiInt, Object> next = it.next();
|
||||
removed = false;
|
||||
Object v = next.getValue();
|
||||
//noinspection unchecked
|
||||
return last = new PointImpl<>(next.getKey(), NULL.equals(v) ? null : (E) v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (last == null || removed) {
|
||||
throw new IllegalStateException(last == null
|
||||
? "Iterator has not yet been called .next() ."
|
||||
: "Iterator item already removed : " + last);
|
||||
}
|
||||
BiInt idx = last.getIdx();
|
||||
SparseArray2D.this.remove(idx.getM(), idx.getN());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ========== private methods ==========
|
||||
|
||||
private int checkLine(int line) {
|
||||
int len = lineLength();
|
||||
if (line < 0 || line >= len) {
|
||||
throw new IndexOutOfBoundsException("Line " + line + " out of bound [0," + (len - 1) + "]");
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private int checkColumn(int column) {
|
||||
int len = columnLength();
|
||||
if (column < 0 || column >= len) {
|
||||
throw new IndexOutOfBoundsException("Column " + column + " out of bound [0," + (len - 1) + "]");
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
private void addIndex(int line, int column) {
|
||||
indexOfLine2columns.computeIfAbsent(line, line1 -> new TreeSet<>(Integer::compareTo)).add(column);
|
||||
indexOfColumn2lines.computeIfAbsent(column, column1 -> new TreeSet<>(Integer::compareTo)).add(line);
|
||||
|
||||
}
|
||||
|
||||
private void removeIndex(int line, int column) {
|
||||
// remove line index
|
||||
{
|
||||
NavigableSet<Integer> columns = indexOfLine2columns.get(line);
|
||||
if (columns == null || !columns.contains(column)) {
|
||||
throw new ConcurrentModificationException(
|
||||
"线程不安全导致索引异常 : lines " + columns + " is null or not contain line " + line);
|
||||
|
||||
}
|
||||
if (columns.size() == 1) {
|
||||
indexOfLine2columns.remove(line);
|
||||
} else {
|
||||
columns.remove(column);
|
||||
}
|
||||
}
|
||||
// remove column index
|
||||
{
|
||||
NavigableSet<Integer> lines = indexOfColumn2lines.get(column);
|
||||
if (lines == null || !lines.contains(line)) {
|
||||
throw new ConcurrentModificationException(
|
||||
"线程不安全导致索引异常 : lines " + lines + " is null or not contain column " + column);
|
||||
}
|
||||
if (lines.size() == 1) {
|
||||
indexOfColumn2lines.remove(column);
|
||||
} else {
|
||||
lines.remove(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.jd.platform.async.openutil.collection;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* id存储柜。
|
||||
* 每个元素的id是固定的(除非取出后重新加入),且id是大于等于0,且分配到的id必须是未分配的id中最小的。
|
||||
* <p>
|
||||
* 类似于我们去游泳馆,里面的存放个人物品的柜子。
|
||||
* 放进去元素后,会分配一个id。然后凭借该id取出元素。
|
||||
* 不过不同于这些现实中的柜子的是,这个存储柜必定会提供最小的id,并且必定>0。
|
||||
* </p>
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/14-上午2:29
|
||||
*/
|
||||
public interface StoreArk<E> extends Iterable<Map.Entry<Integer, E>> {
|
||||
/**
|
||||
* 存入元素
|
||||
*
|
||||
* @param element 元素。
|
||||
* @return 返回最小的id。从0开始。
|
||||
*/
|
||||
int store(E element);
|
||||
|
||||
/**
|
||||
* 查看元素
|
||||
*
|
||||
* @param id id;
|
||||
* @return 返回存在的元素。如果本id未被占用 或 原先存入null,返回null。
|
||||
* @throws IllegalArgumentException id为负数时抛出该异常
|
||||
*/
|
||||
E peek(int id);
|
||||
|
||||
/**
|
||||
* 取出元素
|
||||
*
|
||||
* @param id id
|
||||
* @return 返回被取出的元素。如果本id未被占用 或 原先存入null,返回null。
|
||||
* @throws IllegalArgumentException id为负数时抛出该异常
|
||||
*/
|
||||
E takeOut(int id);
|
||||
|
||||
/**
|
||||
* 元素个数
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* 查找元素的id
|
||||
*
|
||||
* @param element 元素
|
||||
* @return 如果存在,返回id。不存在返回-1
|
||||
*/
|
||||
int findId(E element);
|
||||
|
||||
/**
|
||||
* 返回流
|
||||
*/
|
||||
default Stream<Map.Entry<Integer, E>> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jd.platform.async.openutil.timer;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/12-下午6:36
|
||||
*/
|
||||
public abstract class AbstractWheelTimer implements Timer, AutoCloseable {
|
||||
public static final int WORKER_STATE_INIT = 0;
|
||||
public static final int WORKER_STATE_STARTED = 1;
|
||||
public static final int WORKER_STATE_SHUTDOWN = 2;
|
||||
|
||||
public abstract void start();
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
package com.jd.platform.async.openutil.timer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 从netty里抄来的,删去了一些功能。
|
||||
* <p/>
|
||||
* <b>
|
||||
* 如果违反开源协议,请联系作者: zh.jobs@foxmail.com
|
||||
* If violate the open source agreement, please contact the author : zh0u.he@qq.com
|
||||
* </b>
|
||||
*
|
||||
* @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<HashedWheelTimeout> timeouts = new ConcurrentLinkedDeque<>();
|
||||
private final Queue<HashedWheelTimeout> cancelledTimeouts = new ConcurrentLinkedDeque<>();
|
||||
private final AtomicLong pendingTimeouts = new AtomicLong(0);
|
||||
private final long maxPendingTimeouts;
|
||||
|
||||
private volatile long startTime;
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default thread factory
|
||||
* ({@link Executors#defaultThreadFactory()}), default tick duration, and
|
||||
* default number of ticks per wheel.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public HashedWheelTimer() {
|
||||
this(Executors.defaultThreadFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default thread factory
|
||||
* ({@link Executors#defaultThreadFactory()}) and default number of ticks
|
||||
* per wheel.
|
||||
*
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @throws NullPointerException if {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code tickDuration} is <= 0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public HashedWheelTimer(long tickDuration, TimeUnit unit) {
|
||||
this(Executors.defaultThreadFactory(), tickDuration, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default thread factory
|
||||
* ({@link Executors#defaultThreadFactory()}).
|
||||
*
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @throws NullPointerException if {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) {
|
||||
this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default tick duration and default number of
|
||||
* ticks per wheel.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @throws NullPointerException if {@code threadFactory} is {@code null}
|
||||
*/
|
||||
public HashedWheelTimer(ThreadFactory threadFactory) {
|
||||
this(threadFactory, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default number of ticks per wheel.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code tickDuration} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory, long tickDuration, TimeUnit unit) {
|
||||
this(threadFactory, tickDuration, unit, 512);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory,
|
||||
long tickDuration, TimeUnit unit, int ticksPerWheel) {
|
||||
this(threadFactory, tickDuration, unit, ticksPerWheel, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @param maxPendingTimeouts The maximum number of pending timeouts after which call to
|
||||
* {@code newTimeout} will result in
|
||||
* {@link RejectedExecutionException}
|
||||
* being thrown. No maximum pending timeouts limit is assumed if
|
||||
* this value is 0 or negative.
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(ThreadFactory threadFactory,
|
||||
long tickDuration,
|
||||
TimeUnit unit,
|
||||
int ticksPerWheel,
|
||||
long maxPendingTimeouts) {
|
||||
|
||||
Objects.requireNonNull(threadFactory, "threadFactory must not null !");
|
||||
Objects.requireNonNull(threadFactory, "unit must not null !");
|
||||
if (tickDuration <= 0) {
|
||||
throw new IllegalArgumentException("tickDuration should > 0 !");
|
||||
}
|
||||
if (ticksPerWheel <= 0) {
|
||||
throw new IllegalArgumentException("ticksPerWheel should > 0 !");
|
||||
}
|
||||
|
||||
wheel = createWheel(ticksPerWheel);
|
||||
mask = wheel.length - 1;
|
||||
|
||||
long duration = unit.toNanos(tickDuration);
|
||||
|
||||
// 检查一个周期是否比Long.MAX_VALUE还长
|
||||
if (duration >= Long.MAX_VALUE / wheel.length) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
|
||||
tickDuration, Long.MAX_VALUE / wheel.length));
|
||||
}
|
||||
|
||||
this.tickDuration = Math.max(duration, MILLISECOND_NANOS);
|
||||
workerThread = threadFactory.newThread(worker);
|
||||
this.maxPendingTimeouts = maxPendingTimeouts;
|
||||
}
|
||||
|
||||
private static HashedWheelBucket[] createWheel(int ticksPerWheel) {
|
||||
if (ticksPerWheel <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"ticksPerWheel must be greater than 0: " + ticksPerWheel);
|
||||
}
|
||||
if (ticksPerWheel > 1073741824) {
|
||||
throw new IllegalArgumentException(
|
||||
"ticksPerWheel may not be greater than 2^30: " + ticksPerWheel);
|
||||
}
|
||||
|
||||
ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
|
||||
HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
|
||||
for (int i = 0; i < wheel.length; i++) {
|
||||
wheel[i] = new HashedWheelBucket();
|
||||
}
|
||||
return wheel;
|
||||
}
|
||||
|
||||
private static int normalizeTicksPerWheel(int ticksPerWheel) {
|
||||
int normalizedTicksPerWheel = 1;
|
||||
while (normalizedTicksPerWheel < ticksPerWheel) {
|
||||
normalizedTicksPerWheel <<= 1;
|
||||
}
|
||||
return normalizedTicksPerWheel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显式启动后台线程。即使您没有调用此方法,后台线程也将根据需要自动启动。
|
||||
*
|
||||
* @throws IllegalStateException 如果此计时器已停止{@link #stop()}
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
switch (workerState.get()) {
|
||||
case WORKER_STATE_INIT:
|
||||
if (workerState.compareAndSet(WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
|
||||
workerThread.start();
|
||||
}
|
||||
break;
|
||||
case WORKER_STATE_STARTED:
|
||||
break;
|
||||
case WORKER_STATE_SHUTDOWN:
|
||||
throw new IllegalStateException("cannot be started once stopped");
|
||||
default:
|
||||
throw new Error("Invalid WorkerState");
|
||||
}
|
||||
|
||||
// Wait until the startTime is initialized by the worker.
|
||||
while (startTime == 0) {
|
||||
try {
|
||||
startTimeInitialized.await();
|
||||
} catch (InterruptedException ignore) {
|
||||
// Ignore - it will be ready very soon.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Timeout> 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<Timeout> unprocessedTimeouts = new HashSet<Timeout>();
|
||||
|
||||
private long tick;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Initialize the startTime.
|
||||
startTime = System.nanoTime();
|
||||
if (startTime == 0) {
|
||||
// We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.
|
||||
startTime = 1;
|
||||
}
|
||||
|
||||
// Notify the other threads waiting for the initialization at start().
|
||||
startTimeInitialized.countDown();
|
||||
|
||||
do {
|
||||
final long deadline = waitForNextTick();
|
||||
if (deadline > 0) {
|
||||
int idx = (int) (tick & mask);
|
||||
processCancelledTasks();
|
||||
HashedWheelBucket bucket =
|
||||
wheel[idx];
|
||||
transferTimeoutsToBuckets();
|
||||
bucket.expireTimeouts(deadline);
|
||||
tick++;
|
||||
}
|
||||
} while (workerState.get() == WORKER_STATE_STARTED);
|
||||
|
||||
// Fill the unprocessedTimeouts so we can return them from stop() method.
|
||||
for (HashedWheelBucket bucket : wheel) {
|
||||
bucket.clearTimeouts(unprocessedTimeouts);
|
||||
}
|
||||
for (; ; ) {
|
||||
HashedWheelTimeout timeout = timeouts.poll();
|
||||
if (timeout == null) {
|
||||
break;
|
||||
}
|
||||
if (!timeout.isCancelled()) {
|
||||
unprocessedTimeouts.add(timeout);
|
||||
}
|
||||
}
|
||||
processCancelledTasks();
|
||||
}
|
||||
|
||||
private void transferTimeoutsToBuckets() {
|
||||
// transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just
|
||||
// adds new timeouts in a loop.
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
HashedWheelTimeout timeout = timeouts.poll();
|
||||
if (timeout == null) {
|
||||
// all processed
|
||||
break;
|
||||
}
|
||||
if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) {
|
||||
// Was cancelled in the meantime.
|
||||
continue;
|
||||
}
|
||||
|
||||
long calculated = timeout.deadline / tickDuration;
|
||||
timeout.remainingRounds = (calculated - tick) / wheel.length;
|
||||
|
||||
final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past.
|
||||
int stopIndex = (int) (ticks & mask);
|
||||
|
||||
HashedWheelBucket bucket = wheel[stopIndex];
|
||||
bucket.addTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
private void processCancelledTasks() {
|
||||
for (; ; ) {
|
||||
HashedWheelTimeout timeout = cancelledTimeouts.poll();
|
||||
if (timeout == null) {
|
||||
// all processed
|
||||
break;
|
||||
}
|
||||
try {
|
||||
timeout.remove();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate goal nanoTime from startTime and current tick number,
|
||||
* then wait until that goal has been reached.
|
||||
*
|
||||
* @return Long.MIN_VALUE if received a shutdown request,
|
||||
* current time otherwise (with Long.MIN_VALUE changed by +1)
|
||||
*/
|
||||
private long waitForNextTick() {
|
||||
long deadline = tickDuration * (tick + 1);
|
||||
|
||||
for (; ; ) {
|
||||
final long currentTime = System.nanoTime() - startTime;
|
||||
long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;
|
||||
|
||||
if (sleepTimeMs <= 0) {
|
||||
if (currentTime == Long.MIN_VALUE) {
|
||||
return -Long.MAX_VALUE;
|
||||
} else {
|
||||
return currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(sleepTimeMs);
|
||||
} catch (InterruptedException ignored) {
|
||||
if (workerState.get() == WORKER_STATE_SHUTDOWN) {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Timeout> 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<HashedWheelTimeout> 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<Timeout> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jd.platform.async.openutil.timer;
|
||||
|
||||
/**
|
||||
* 借鉴netty。
|
||||
* 一个连接着{@link Timer}和{@link TimerTask},表示着任务状态的“关系类”
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午6:33
|
||||
*/
|
||||
public interface Timeout {
|
||||
/**
|
||||
* 返回对应的{@link Timer}。
|
||||
*/
|
||||
Timer timer();
|
||||
|
||||
/**
|
||||
* 返回对应的{@link TimerTask}
|
||||
*/
|
||||
TimerTask task();
|
||||
|
||||
/**
|
||||
* 当且仅当关联的{@link TimerTask}已超时时,才返回{@code true}。
|
||||
*/
|
||||
boolean isExpired();
|
||||
|
||||
/**
|
||||
* 当且仅当关联的{@link TimerTask}被取消时,才返回{@code true}。
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* 尝试取消关联的{@link TimerTask}。如果任务已经执行或已取消,它将无副作用地返回。
|
||||
*
|
||||
* @return 如果取消成功完成,则为true,否则为false
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
boolean cancel();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jd.platform.async.openutil.timer;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 照抄netty
|
||||
* 让{@link TimerTask}在后台线程中执行。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午6:33
|
||||
*/
|
||||
public interface Timer {
|
||||
|
||||
/**
|
||||
* 使{@link TimerTask}在指定的延迟后执行一次。
|
||||
*
|
||||
* @param delay 延时长度
|
||||
* @param unit 延时单位
|
||||
* @return 返回 {@link Timeout}关系类
|
||||
* @throws IllegalStateException 如果此计时器已经已停止
|
||||
* @throws RejectedExecutionException 如果挂起的超时太多,则创建新的超时会导致系统不稳定。
|
||||
*/
|
||||
Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
default Timeout newTimeout(Runnable runnable, long delay, TimeUnit unit) {
|
||||
AtomicReference<Timeout> timeoutRef = new AtomicReference<>();
|
||||
newTimeout(timeout -> {
|
||||
timeoutRef.set(timeout);
|
||||
runnable.run();
|
||||
}, delay, unit);
|
||||
return timeoutRef.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放此{@link Timer}所有资源(例如线程),并取消所有尚未执行的任务。
|
||||
*
|
||||
* @return 与被该方法取消的任务相关联的 {@link Timeout}
|
||||
*/
|
||||
Set<? extends Timeout> stop();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jd.platform.async.openutil.timer;
|
||||
|
||||
/**
|
||||
* 类似于netty的TimerTask。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午5:17
|
||||
*/
|
||||
public interface TimerTask{
|
||||
void run(Timeout timeout) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package openutiltest;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* 便于测试的jdk动态代理
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/16-下午11:38
|
||||
*/
|
||||
public class PrintProxy<I> {
|
||||
public PrintProxy(Class<I> clazz) {
|
||||
this.interfaceClazz = clazz;
|
||||
}
|
||||
|
||||
private final Class<?> interfaceClazz;
|
||||
|
||||
public I proxyTo(I obj, String objNickName) {
|
||||
//noinspection unchecked
|
||||
return (I) Proxy.newProxyInstance(
|
||||
obj.getClass().getClassLoader(),
|
||||
new Class[]{interfaceClazz},
|
||||
(proxy, method, args) -> {
|
||||
String methodInfo = methodInfo(method);
|
||||
try {
|
||||
Object res = method.invoke(obj, args);
|
||||
System.out.printf(objNickName + " 执行方法: %-40s --> 方法返回值: %-20s --> this.toString() = %-40s\n",
|
||||
methodInfo, res, obj);
|
||||
return res;
|
||||
} catch (Exception e) {
|
||||
System.err.printf(objNickName + " 执行方法: %-40s --> 异常信息: %-40s --> this.toString() = %-40s\n",
|
||||
methodInfo, e.getClass().getSimpleName() + " : " + e.getMessage(), obj
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static String methodInfo(Method method) {
|
||||
StringBuilder sb = new StringBuilder().append(method.getName()).append('(');
|
||||
for (Class<?> parameterType : method.getParameterTypes()) {
|
||||
sb.append(parameterType.getSimpleName()).append(", ");
|
||||
}
|
||||
if (method.getParameterTypes().length > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
return sb.append(')').toString();
|
||||
}
|
||||
|
||||
}
|
||||
37
asyncTool-openutil/src/test/java/openutiltest/TestGraph.java
Normal file
37
asyncTool-openutil/src/test/java/openutiltest/TestGraph.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package openutiltest;
|
||||
|
||||
import com.jd.platform.async.openutil.collection.CommonDirectedGraph;
|
||||
import com.jd.platform.async.openutil.collection.DirectedGraph;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 测试图工具类的使用
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/16-下午11:25
|
||||
*/
|
||||
class TestGraph {
|
||||
public static void main(String[] args) {
|
||||
test_CommonDirectedGraph();
|
||||
}
|
||||
|
||||
private static void test_CommonDirectedGraph() {
|
||||
System.out.println("\n\n ==================== 测试正常使用 ==================");
|
||||
//noinspection unchecked
|
||||
DirectedGraph<String, String> graph =
|
||||
new PrintProxy<>(DirectedGraph.class).proxyTo(new CommonDirectedGraph<>(), "graph");
|
||||
graph.isDirected();
|
||||
graph.addNode("胖虎");
|
||||
graph.addNode("大雄");
|
||||
graph.putRelation("胖虎", "打", "大雄");
|
||||
graph.addNode("静香");
|
||||
graph.nodesView().addAll(Arrays.asList("小夫", "胖虎的妹妹", "哆啦A梦"));
|
||||
graph.putRelation("胖虎", "是其哥", "胖虎的妹妹");
|
||||
graph.putRelation("胖虎的妹妹", "是其妹", "胖虎");
|
||||
graph.putRelation("胖虎的妹妹", "喜欢", "大雄");
|
||||
graph.putRelation("胖虎", "????", "小夫");
|
||||
graph.putRelation("大雄", "喜欢", "静香");
|
||||
graph.removeNode("大雄");
|
||||
graph.getRelations();
|
||||
}
|
||||
}
|
||||
7
pom.xml
7
pom.xml
@@ -6,7 +6,8 @@
|
||||
|
||||
<groupId>com.jd.platform</groupId>
|
||||
<artifactId>asyncTool</artifactId>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.5.1-SNAPSHOT</version>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -21,4 +22,8 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modules>
|
||||
<module>asyncTool-openutil</module>
|
||||
<module>asyncTool-core</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.jd.platform.async.callback;
|
||||
|
||||
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
|
||||
/**
|
||||
* 每个执行单元执行完毕后,会回调该接口</p>
|
||||
* 需要监听执行结果的,实现该接口即可
|
||||
*
|
||||
* @author wuweifeng wrote on 2019-11-19.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ICallback<T, V> {
|
||||
|
||||
/**
|
||||
* 任务开始的监听
|
||||
*/
|
||||
default void begin() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 耗时操作执行完毕后,就给value注入值
|
||||
* <p/>
|
||||
* 只要Wrapper被调用后成功或失败/超时,该方法都会被执行。
|
||||
*/
|
||||
void result(boolean success, T param, WorkResult<V> workResult);
|
||||
}
|
||||
@@ -1,720 +0,0 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.callback.DefaultCallback;
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.exception.SkippedException;
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
import com.jd.platform.async.worker.*;
|
||||
import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependMustStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependWrapperStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependenceAction;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 对每个worker及callback进行包装,一对一
|
||||
* <p/>
|
||||
* v1.5时将其抽取为抽象类,以解耦并提高扩展性。
|
||||
*
|
||||
* @author wuweifeng wrote on 2019-11-19.
|
||||
*/
|
||||
public abstract class WorkerWrapper<T, V> {
|
||||
/**
|
||||
* 该wrapper的唯一标识
|
||||
*/
|
||||
protected final String id;
|
||||
/**
|
||||
* worker将来要处理的param
|
||||
*/
|
||||
protected T param;
|
||||
protected IWorker<T, V> worker;
|
||||
protected ICallback<T, V> callback;
|
||||
/**
|
||||
* 标记该事件是否已经被处理过了,譬如已经超时返回false了,后续rpc又收到返回值了,则不再二次回调
|
||||
* 经试验,volatile并不能保证"同一毫秒"内,多线程对该值的修改和拉取
|
||||
* <p>
|
||||
* 1-finish, 2-error, 3-working
|
||||
*/
|
||||
protected final AtomicInteger state = new AtomicInteger(0);
|
||||
/**
|
||||
* 也是个钩子变量,用来存临时的结果
|
||||
*/
|
||||
protected volatile WorkResult<V> workResult = WorkResult.defaultResult();
|
||||
/**
|
||||
* 该map存放所有wrapper的id和wrapper映射
|
||||
* <p/>
|
||||
* 需要线程安全。
|
||||
*/
|
||||
private Map<String, WorkerWrapper<?, ?>> forParamUseWrappers;
|
||||
/**
|
||||
* 各种策略的封装类。
|
||||
* <p/>
|
||||
* 其实是因为加功能太多导致这个对象大小超过了128Byte,所以强迫症的我不得不把几个字段丢到策略类里面去。
|
||||
* ps: 大小超过128Byte令我(TcSnZh)难受的一比,就像走在草坪的格子上,一步嫌小、两步扯蛋。
|
||||
* IDEA可以使用JOL Java Object Layout插件查看对象大小。
|
||||
*/
|
||||
private final WrapperStrategy wrapperStrategy = new WrapperStrategy();
|
||||
/**
|
||||
* 超时检查,该值允许为null。表示不设置。
|
||||
*/
|
||||
private volatile TimeOutProperties timeOutProperties;
|
||||
|
||||
// ***** state属性的常量值 *****
|
||||
|
||||
public static final int FINISH = 1;
|
||||
public static final int ERROR = 2;
|
||||
public static final int WORKING = 3;
|
||||
public static final int INIT = 0;
|
||||
|
||||
WorkerWrapper(String id, IWorker<T, V> worker, T param, ICallback<T, V> callback) {
|
||||
if (worker == null) {
|
||||
throw new NullPointerException("async.worker is null");
|
||||
}
|
||||
this.worker = worker;
|
||||
this.param = param;
|
||||
this.id = id;
|
||||
//允许不设置回调
|
||||
if (callback == null) {
|
||||
callback = new DefaultCallback<>();
|
||||
}
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
// ========== public ==========
|
||||
|
||||
/**
|
||||
* 外部调用本线程运行此Wrapper的入口方法。
|
||||
*
|
||||
* @param executorService 该ExecutorService将成功运行后,在nextWrapper有多个时被使用于多线程调用。
|
||||
* @param remainTime 剩下的时间
|
||||
* @param forParamUseWrappers 用于保存经过的wrapper的信息的Map,key为id。
|
||||
* @param inspector wrapper调度检查器
|
||||
*/
|
||||
public void work(ExecutorService executorService,
|
||||
long remainTime,
|
||||
Map<String, WorkerWrapper<?, ?>> forParamUseWrappers,
|
||||
WrapperEndingInspector inspector) {
|
||||
work(executorService, null, remainTime, forParamUseWrappers, inspector);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public WorkResult<V> getWorkResult() {
|
||||
return workResult;
|
||||
}
|
||||
|
||||
public void setParam(T param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取之后的下游Wrapper
|
||||
*/
|
||||
public abstract Set<WorkerWrapper<?, ?>> getNextWrappers();
|
||||
|
||||
/**
|
||||
* 使wrapper状态修改为超时失败。(但如果已经执行完成则不会修改)
|
||||
* <p/>
|
||||
* 本方法不会试图执行超时判定逻辑。
|
||||
* 如果要执行超时逻辑判断,请用{@link TimeOutProperties#checkTimeOut(boolean)}并传入参数true。
|
||||
*/
|
||||
public void failNow() {
|
||||
int state = getState();
|
||||
if (state == INIT || state == WORKING) {
|
||||
fastFail(state, null);
|
||||
}
|
||||
}
|
||||
|
||||
public WrapperStrategy getWrapperStrategy() {
|
||||
return wrapperStrategy;
|
||||
}
|
||||
|
||||
// ========== protected ==========
|
||||
|
||||
/**
|
||||
* 快速失败
|
||||
*
|
||||
* @return 已经失败则返回false,如果刚才设置为失败了则返回true。
|
||||
*/
|
||||
protected boolean fastFail(int expect, Exception e) {
|
||||
//试图将它从expect状态,改成Error
|
||||
if (!compareAndSetState(expect, ERROR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//尚未处理过结果
|
||||
if (checkIsNullResult()) {
|
||||
if (e == null) {
|
||||
workResult.setResultState(ResultState.TIMEOUT);
|
||||
} else {
|
||||
workResult.setResultState(ResultState.EXCEPTION);
|
||||
workResult.setEx(e);
|
||||
}
|
||||
workResult.setResult(worker.defaultValue());
|
||||
}
|
||||
callback.result(false, param, workResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断{@link #state}状态是否是初始值。
|
||||
*/
|
||||
protected boolean checkIsNullResult() {
|
||||
return ResultState.DEFAULT == workResult.getResultState();
|
||||
}
|
||||
|
||||
protected boolean compareAndSetState(int expect, int update) {
|
||||
return this.state.compareAndSet(expect, update);
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作的核心方法。
|
||||
*
|
||||
* @param fromWrapper 代表这次work是由哪个上游wrapper发起的。如果是首个Wrapper则为null。
|
||||
* @param remainTime 剩余时间。
|
||||
*/
|
||||
protected void work(ExecutorService executorService,
|
||||
WorkerWrapper<?, ?> fromWrapper,
|
||||
long remainTime,
|
||||
Map<String, WorkerWrapper<?, ?>> forParamUseWrappers,
|
||||
WrapperEndingInspector inspector) {
|
||||
this.setForParamUseWrappers(forParamUseWrappers);
|
||||
//将自己放到所有wrapper的集合里去
|
||||
forParamUseWrappers.put(id, this);
|
||||
long now = SystemClock.now();
|
||||
//总的已经超时了,就快速失败,进行下一个
|
||||
if (remainTime <= 0) {
|
||||
fastFail(INIT, null);
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
return;
|
||||
}
|
||||
//如果自己已经执行过了。
|
||||
//可能有多个依赖,其中的一个依赖已经执行完了,并且自己也已开始执行或执行完毕。当另一个依赖执行完毕,又进来该方法时,就不重复处理了
|
||||
if (getState() == FINISH || getState() == ERROR) {
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断是否要跳过自己,该方法可能会跳过正在工作的自己。
|
||||
final WrapperStrategy wrapperStrategy = getWrapperStrategy();
|
||||
if (wrapperStrategy.shouldSkip(getNextWrappers(), this, fromWrapper)) {
|
||||
fastFail(INIT, new SkippedException());
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
return;
|
||||
}
|
||||
|
||||
//如果没有任何依赖,说明自己就是第一批要执行的
|
||||
final Set<WorkerWrapper<?, ?>> dependWrappers = getDependWrappers();
|
||||
if (dependWrappers == null || dependWrappers.size() == 0) {
|
||||
fire();
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
return;
|
||||
}
|
||||
|
||||
DependenceAction.WithProperty judge = wrapperStrategy.judgeAction(dependWrappers, this, fromWrapper);
|
||||
|
||||
switch (judge.getDependenceAction()) {
|
||||
case TAKE_REST:
|
||||
inspector.reduceWrapper(this);
|
||||
return;
|
||||
case FAST_FAIL:
|
||||
switch (judge.getResultState()) {
|
||||
case TIMEOUT:
|
||||
fastFail(INIT, null);
|
||||
break;
|
||||
case EXCEPTION:
|
||||
fastFail(INIT, judge.getFastFailException());
|
||||
break;
|
||||
default:
|
||||
fastFail(INIT, new RuntimeException("ResultState " + judge.getResultState() + " set to FAST_FAIL"));
|
||||
break;
|
||||
}
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
break;
|
||||
case START_WORK:
|
||||
fire();
|
||||
beginNext(executorService, now, remainTime, inspector);
|
||||
break;
|
||||
case JUDGE_BY_AFTER:
|
||||
default:
|
||||
inspector.reduceWrapper(this);
|
||||
throw new IllegalStateException("策略配置错误,不应当在WorkerWrapper中返回JUDGE_BY_AFTER或其他无效值 : this=" + this + ",fromWrapper=" + fromWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行下一个任务
|
||||
*/
|
||||
protected void beginNext(ExecutorService executorService, long now, long remainTime, WrapperEndingInspector inspector) {
|
||||
//花费的时间
|
||||
final long costTime = SystemClock.now() - now;
|
||||
final long nextRemainTIme = remainTime - costTime;
|
||||
Set<WorkerWrapper<?, ?>> nextWrappers = getNextWrappers();
|
||||
if (nextWrappers == null) {
|
||||
inspector.setWrapperEndWithTryPolling(this);
|
||||
return;
|
||||
}
|
||||
// nextWrappers只有一个,就用本线程继续跑。
|
||||
if (nextWrappers.size() == 1) {
|
||||
try {
|
||||
WorkerWrapper<?, ?> next = nextWrappers.stream().findFirst().get();
|
||||
inspector.addWrapper(next);
|
||||
next.work(executorService, WorkerWrapper.this, nextRemainTIme, getForParamUseWrappers(), inspector);
|
||||
} finally {
|
||||
inspector.setWrapperEndWithTryPolling(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// nextWrappers有多个
|
||||
try {
|
||||
inspector.addWrapper(nextWrappers);
|
||||
nextWrappers.forEach(next -> executorService.submit(() ->
|
||||
next.work(executorService, this, nextRemainTIme, getForParamUseWrappers(), inspector))
|
||||
);
|
||||
} finally {
|
||||
inspector.setWrapperEndWithTryPolling(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本工作线程执行自己的job.判断阻塞超时这里开始时会判断一次总超时时间,但在轮询线程会判断单个wrapper超时时间,并也会判断总超时时间。
|
||||
*/
|
||||
protected void fire() {
|
||||
//阻塞取结果
|
||||
//避免重复执行
|
||||
if (!checkIsNullResult()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
//如果已经不是init状态了,说明正在被执行或已执行完毕。这一步很重要,可以保证任务不被重复执行
|
||||
if (!compareAndSetState(INIT, WORKING)) {
|
||||
return;
|
||||
}
|
||||
V resultValue;
|
||||
try {
|
||||
callback.begin();
|
||||
if (timeOutProperties != null) {
|
||||
timeOutProperties.startWorking();
|
||||
}
|
||||
//执行耗时操作
|
||||
resultValue = (V) worker.action(param, (Map) getForParamUseWrappers());
|
||||
} finally {
|
||||
if (timeOutProperties != null) {
|
||||
timeOutProperties.endWorking();
|
||||
}
|
||||
}
|
||||
//如果状态不是在working,说明别的地方已经修改了
|
||||
if (!compareAndSetState(WORKING, FINISH)) {
|
||||
return;
|
||||
}
|
||||
workResult.setResultState(ResultState.SUCCESS);
|
||||
workResult.setResult(resultValue);
|
||||
//回调成功
|
||||
callback.result(true, param, workResult);
|
||||
} catch (Exception e) {
|
||||
//避免重复回调
|
||||
if (!checkIsNullResult()) {
|
||||
return;
|
||||
}
|
||||
fastFail(WORKING, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== hashcode and equals ==========
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code return id.hashCode();}返回id值的hashcode
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// final String id can use to .hashcode() .
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
// ========== Builder ==========
|
||||
|
||||
public static <T, V> WorkerWrapperBuilder<T, V> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自v1.5,该类被抽取到{@link StableWorkerWrapperBuilder}抽象类,兼容之前的版本。
|
||||
*/
|
||||
public static class Builder<W, C> extends StableWorkerWrapperBuilder<W, C, Builder<W, C>> {
|
||||
/**
|
||||
* @deprecated 建议使用 {@link #builder()}返回{@link WorkerWrapperBuilder}接口,以调用v1.5之后的规范api
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== package access methods , for example , some getter/setter that doesn't want to be public ==========
|
||||
|
||||
T getParam() {
|
||||
return param;
|
||||
}
|
||||
|
||||
IWorker<T, V> getWorker() {
|
||||
return worker;
|
||||
}
|
||||
|
||||
void setWorker(IWorker<T, V> worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
ICallback<T, V> getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
void setCallback(ICallback<T, V> callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
void setState(int state) {
|
||||
this.state.set(state);
|
||||
}
|
||||
|
||||
Map<String, WorkerWrapper<?, ?>> getForParamUseWrappers() {
|
||||
return forParamUseWrappers;
|
||||
}
|
||||
|
||||
void setForParamUseWrappers(Map<String, WorkerWrapper<?, ?>> forParamUseWrappers) {
|
||||
this.forParamUseWrappers = forParamUseWrappers;
|
||||
}
|
||||
|
||||
void setWorkResult(WorkResult<V> workResult) {
|
||||
this.workResult = workResult;
|
||||
}
|
||||
|
||||
abstract void setNextWrappers(Set<WorkerWrapper<?, ?>> nextWrappers);
|
||||
|
||||
abstract Set<WorkerWrapper<?, ?>> getDependWrappers();
|
||||
|
||||
abstract void setDependWrappers(Set<WorkerWrapper<?, ?>> dependWrappers);
|
||||
|
||||
TimeOutProperties getTimeOut() {
|
||||
return timeOutProperties;
|
||||
}
|
||||
|
||||
void setTimeOut(TimeOutProperties timeOutProperties) {
|
||||
this.timeOutProperties = timeOutProperties;
|
||||
}
|
||||
|
||||
// ========== toString ==========
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(200)
|
||||
.append("WorkerWrapper{id=").append(id)
|
||||
.append(", param=").append(param)
|
||||
.append(", worker=").append(worker)
|
||||
.append(", callback=").append(callback)
|
||||
.append(", state=");
|
||||
int state = this.state.get();
|
||||
if (state == FINISH) {
|
||||
sb.append("FINISH");
|
||||
} else if (state == WORKING) {
|
||||
sb.append("WORKING");
|
||||
} else if (state == INIT) {
|
||||
sb.append("INIT");
|
||||
} else if (state == ERROR) {
|
||||
sb.append("ERROR");
|
||||
} else {
|
||||
throw new IllegalStateException("unknown state : " + state);
|
||||
}
|
||||
sb
|
||||
.append(", workResult=").append(workResult)
|
||||
// 防止循环引用,这里只输出相关Wrapper的id
|
||||
.append(", forParamUseWrappers::getId=[");
|
||||
getForParamUseWrappers().keySet().forEach(wrapperId -> sb.append(wrapperId).append(", "));
|
||||
if (getForParamUseWrappers().keySet().size() > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
sb
|
||||
.append("], dependWrappers::getId=[");
|
||||
getDependWrappers().stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", "));
|
||||
if (getDependWrappers().size() > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
sb
|
||||
.append("], nextWrappers::getId=[");
|
||||
getNextWrappers().stream().map(WorkerWrapper::getId).forEach(wrapperId -> sb.append(wrapperId).append(", "));
|
||||
if (getNextWrappers().size() > 0) {
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
}
|
||||
sb
|
||||
.append("]")
|
||||
.append(", wrapperStrategy=").append(getWrapperStrategy())
|
||||
.append(", timeOutProperties=").append(getTimeOut())
|
||||
.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class WrapperStrategy implements DependenceStrategy, SkipStrategy {
|
||||
|
||||
// ========== 这三个属性用来判断是否要开始工作 ==========
|
||||
|
||||
// 从前往后依次判断的顺序为 dependWrapperStrategyMapper -> dependMustStrategyMapper -> dependenceStrategy
|
||||
|
||||
/**
|
||||
* 对特殊Wrapper专用的依赖响应策略。
|
||||
* <b>该值允许为null</b>
|
||||
*/
|
||||
private DependWrapperStrategyMapper dependWrapperStrategyMapper;
|
||||
/**
|
||||
* 对必须完成的(must的)Wrapper的依赖响应策略。
|
||||
* <b>该值允许为null</b>
|
||||
* <p/>
|
||||
* 这是一个不得不向历史妥协的属性。用于适配must开关方式。
|
||||
*/
|
||||
private DependMustStrategyMapper dependMustStrategyMapper;
|
||||
/**
|
||||
* 依赖响应全局策略。
|
||||
*/
|
||||
private DependenceStrategy dependenceStrategy;
|
||||
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper) {
|
||||
// 如果存在依赖,则调用三层依赖响应策略进行判断
|
||||
DependenceStrategy strategy = dependWrapperStrategyMapper;
|
||||
if (dependMustStrategyMapper != null) {
|
||||
strategy = strategy == null ? dependMustStrategyMapper : strategy.thenJudge(dependMustStrategyMapper);
|
||||
}
|
||||
if (dependenceStrategy != null) {
|
||||
strategy = strategy == null ? dependenceStrategy : strategy.thenJudge(dependenceStrategy);
|
||||
}
|
||||
if (strategy == null) {
|
||||
throw new IllegalStateException("配置无效,三层判断策略均为null,请开发者检查自己的Builder是否逻辑错误!");
|
||||
}
|
||||
return strategy.judgeAction(dependWrappers, thisWrapper, fromWrapper);
|
||||
}
|
||||
|
||||
public DependWrapperStrategyMapper getDependWrapperStrategyMapper() {
|
||||
return dependWrapperStrategyMapper;
|
||||
}
|
||||
|
||||
public void setDependWrapperStrategyMapper(DependWrapperStrategyMapper dependWrapperStrategyMapper) {
|
||||
this.dependWrapperStrategyMapper = dependWrapperStrategyMapper;
|
||||
}
|
||||
|
||||
public DependMustStrategyMapper getDependMustStrategyMapper() {
|
||||
return dependMustStrategyMapper;
|
||||
}
|
||||
|
||||
public void setDependMustStrategyMapper(DependMustStrategyMapper dependMustStrategyMapper) {
|
||||
this.dependMustStrategyMapper = dependMustStrategyMapper;
|
||||
}
|
||||
|
||||
public DependenceStrategy getDependenceStrategy() {
|
||||
return dependenceStrategy;
|
||||
}
|
||||
|
||||
public void setDependenceStrategy(DependenceStrategy dependenceStrategy) {
|
||||
this.dependenceStrategy = dependenceStrategy;
|
||||
}
|
||||
|
||||
// ========== 跳过策略 ==========
|
||||
|
||||
private SkipStrategy skipStrategy;
|
||||
|
||||
@Override
|
||||
public boolean shouldSkip(Set<WorkerWrapper<?, ?>> nextWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper) {
|
||||
return skipStrategy != null && skipStrategy.shouldSkip(nextWrappers, thisWrapper, fromWrapper);
|
||||
}
|
||||
|
||||
public SkipStrategy getSkipStrategy() {
|
||||
return skipStrategy;
|
||||
}
|
||||
|
||||
public void setSkipStrategy(SkipStrategy skipStrategy) {
|
||||
this.skipStrategy = skipStrategy;
|
||||
}
|
||||
|
||||
// ========== toString ==========
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrapperStrategy{" +
|
||||
"dependWrapperStrategyMapper=" + dependWrapperStrategyMapper +
|
||||
", dependMustStrategyMapper=" + dependMustStrategyMapper +
|
||||
", dependenceStrategy=" + dependenceStrategy +
|
||||
", skipStrategy=" + skipStrategy +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeOutProperties {
|
||||
private final boolean enable;
|
||||
private final long time;
|
||||
private final TimeUnit unit;
|
||||
private final boolean allowInterrupt;
|
||||
private final WorkerWrapper<?, ?> wrapper;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private volatile boolean started = false;
|
||||
private volatile boolean ended = false;
|
||||
private volatile long startWorkingTime;
|
||||
private volatile long endWorkingTime;
|
||||
private volatile Thread doWorkingThread;
|
||||
|
||||
public TimeOutProperties(boolean enable, long time, TimeUnit unit, boolean allowInterrupt, WorkerWrapper<?, ?> wrapper) {
|
||||
this.enable = enable;
|
||||
this.time = time;
|
||||
this.unit = unit;
|
||||
this.allowInterrupt = allowInterrupt;
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
|
||||
// ========== 工作线程调用 ==========
|
||||
|
||||
public void startWorking() {
|
||||
synchronized (lock) {
|
||||
started = true;
|
||||
startWorkingTime = SystemClock.now();
|
||||
doWorkingThread = Thread.currentThread();
|
||||
}
|
||||
}
|
||||
|
||||
public void endWorking() {
|
||||
synchronized (lock) {
|
||||
ended = true;
|
||||
doWorkingThread = null;
|
||||
endWorkingTime = SystemClock.now();
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 轮询线程调用 ==========
|
||||
|
||||
/**
|
||||
* 检查超时。
|
||||
* 可以将boolean参数传入true以在超时的时候直接失败。
|
||||
*
|
||||
* @param withStop 如果为false,不会发生什么,仅仅是单纯的判断是否超时。
|
||||
* 如果为true,则会去快速失败wrapper{@link #failNow()},有必要的话还会打断线程。
|
||||
* @return 如果 超时 或 执行时间超过限制 返回true;未超时返回false。
|
||||
*/
|
||||
public boolean checkTimeOut(boolean withStop) {
|
||||
if (enable) {
|
||||
synchronized (lock) {
|
||||
if (started) {
|
||||
// 判断执行中的wrapper是否超时
|
||||
long dif = (ended ? endWorkingTime : SystemClock.now()) - startWorkingTime;
|
||||
if (dif > unit.toMillis(time)) {
|
||||
if (withStop) {
|
||||
if (allowInterrupt) {
|
||||
doWorkingThread.interrupt();
|
||||
}
|
||||
wrapper.failNow();
|
||||
ended = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ========== package ==========
|
||||
|
||||
boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
TimeUnit getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
boolean isAllowInterrupt() {
|
||||
return allowInterrupt;
|
||||
}
|
||||
|
||||
Object getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
void setStarted(boolean started) {
|
||||
this.started = started;
|
||||
}
|
||||
|
||||
boolean isEnded() {
|
||||
return ended;
|
||||
}
|
||||
|
||||
void setEnded(boolean ended) {
|
||||
this.ended = ended;
|
||||
}
|
||||
|
||||
long getStartWorkingTime() {
|
||||
return startWorkingTime;
|
||||
}
|
||||
|
||||
void setStartWorkingTime(long startWorkingTime) {
|
||||
this.startWorkingTime = startWorkingTime;
|
||||
}
|
||||
|
||||
long getEndWorkingTime() {
|
||||
return endWorkingTime;
|
||||
}
|
||||
|
||||
void setEndWorkingTime(long endWorkingTime) {
|
||||
this.endWorkingTime = endWorkingTime;
|
||||
}
|
||||
|
||||
Thread getDoWorkingThread() {
|
||||
return doWorkingThread;
|
||||
}
|
||||
|
||||
void setDoWorkingThread(Thread doWorkingThread) {
|
||||
this.doWorkingThread = doWorkingThread;
|
||||
}
|
||||
|
||||
|
||||
// ========== toString ==========
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeOutProperties{" +
|
||||
"enable=" + enable +
|
||||
", time=" + time +
|
||||
", unit=" + unit +
|
||||
", allowInterrupt=" + allowInterrupt +
|
||||
", wrapper::getId=" + wrapper.getId() +
|
||||
", started=" + started +
|
||||
", ended=" + ended +
|
||||
", startWorkingTime=" + startWorkingTime +
|
||||
", endWorkingTime=" + endWorkingTime +
|
||||
", doWorkingThread=" + doWorkingThread +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}是否链路调用完成的轮询器。
|
||||
* =================================================================================
|
||||
* <p>
|
||||
* 在v1.4及以前的版本,存在如下问题:
|
||||
* >
|
||||
* 在使用线程数量较少的线程池进行beginWork时,调用WorkerWrapper#beginNext方法时,
|
||||
* 会因为本线程等待下游Wrapper执行完成而存在线程耗尽bug。线程池会死翘翘的僵住、动弹不得。
|
||||
* >
|
||||
* 例如仅有2个线程的线程池,执行以下任务:
|
||||
* {@code
|
||||
* <p>
|
||||
* 这是旧版本(v1.4及以前)中可能会引发线程耗尽bug的情况,在test/v15.dependnew中示例testThreadPolling_V14Bug说明了这个bug
|
||||
* 线程数:2
|
||||
* A(5ms)--B1(10ms) ---|--> C1(5ms)
|
||||
* . \ | (B1、B2全部完成可执行C1、C2)
|
||||
* . ---> B2(20ms) --|--> C2(5ms)
|
||||
* <p>
|
||||
* }
|
||||
* 线程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}进行检查是否完成。
|
||||
* </p>
|
||||
* =================================================================================
|
||||
* <p>
|
||||
* 本类的工作原理:
|
||||
* .
|
||||
* 原理:
|
||||
* (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时,即可判断出链路执行完毕了。
|
||||
* </p>
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/5-下午3:22
|
||||
*/
|
||||
public class WrapperEndingInspector implements Comparable<WrapperEndingInspector> {
|
||||
/**
|
||||
* 最迟完成时间
|
||||
*/
|
||||
private final long latestFinishTime;
|
||||
|
||||
/**
|
||||
* 保存 需要检查的wrapper--相关属性 的Map。
|
||||
*/
|
||||
private final ConcurrentHashMap<WorkerWrapper, WrapperNode> wrappers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 当全部wrapper都调用结束,它会countDown
|
||||
*/
|
||||
private final CountDownLatch endCDL = new CountDownLatch(1);
|
||||
|
||||
/**
|
||||
* 读锁用于修改数据,写锁用于轮询。使用公平锁让wrapper的时间波动不会太长。
|
||||
* <p/>
|
||||
* 在轮询到本inspector时,之所以要上写锁,是因为:
|
||||
* 假如此时有个Wrapper正在调用{@link #addWrapper(WorkerWrapper)},则wrappers发生了改变。
|
||||
* 假如现在恰巧访问到的是{@link #wrappers}迭代器的最后一个,但此时又加入了另一个,且这另一个又是需要去执行的。
|
||||
* 那么假如在迭代器遍历到目前访问到的wrapper都是呼叫完毕的,那么这新加入的一个就会被忽略,从而判定为全部完成。致使bug发生。
|
||||
* <p/>
|
||||
* 此外,即便轮询时上写锁,对性能的影响也是有限的。因为这只会在“呼叫别人”的时候发生工作线程与轮询线程的锁争抢,
|
||||
* 而在工作线程执行{@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<? extends WorkerWrapper> 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已经呼叫完成了。
|
||||
* <p/>
|
||||
* 该方法会调用{@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}中。
|
||||
* <p/>
|
||||
* 当试图把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() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮询中心。具体的轮询调度由其完成。
|
||||
* <p/>
|
||||
* {@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<WrapperEndingInspector, InspectorNode> 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<WrapperEndingInspector, InspectorNode> 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<WorkerWrapper, WrapperNode> 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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<WorkerWrapper<?, ?>> nextWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper);
|
||||
|
||||
/**
|
||||
* 不跳过
|
||||
*/
|
||||
SkipStrategy NOT_SKIP = new SkipStrategy() {
|
||||
@Override
|
||||
public boolean shouldSkip(Set<WorkerWrapper<?, ?>> 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<WorkerWrapper<?, ?>> 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<WorkerWrapper<?, ?>> nextWrappers, WorkerWrapper<?, ?> thisWrapper, WorkerWrapper<?, ?> fromWrapper) {
|
||||
Set<WorkerWrapper<?, ?>> 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<BfsNode> queue = nextSet.stream().map(ww -> new BfsNode(ww, 0)).collect(Collectors.toCollection(LinkedList::new));
|
||||
HashSet<WorkerWrapper<?, ?>> 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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user