mirror of
https://gitee.com/jd-platform-opensource/asyncTool.git
synced 2026-03-22 04:27:15 +08:00
!9 重构v1.5.1的不良代码,更新了QuickStart文档
Merge pull request !9 from tcsn_site/dev-tcsnzh-arrangement
This commit is contained in:
1290
QuickStart.md
1290
QuickStart.md
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ 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);
|
||||
|
||||
/**
|
||||
* 超时、异常时,返回的默认值
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.jd.platform.async.exception;
|
||||
|
||||
/**
|
||||
* 如果任务在执行之前,自己后面的任务已经执行完或正在被执行,则抛该exception
|
||||
*
|
||||
* @author wuweifeng wrote on 2020-02-18
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.jd.platform.async.callback.DefaultGroupCallback;
|
||||
import com.jd.platform.async.callback.IGroupCallback;
|
||||
import com.jd.platform.async.executor.timer.SystemClock;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WrapperEndingInspector;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
@@ -29,32 +29,29 @@ public class Async {
|
||||
*/
|
||||
public static boolean beginWork(long timeout,
|
||||
ExecutorService executorService,
|
||||
Collection<? extends WorkerWrapper<?,?>> workerWrappers)
|
||||
Collection<? extends WorkerWrapper<?, ?>> workerWrappers)
|
||||
throws InterruptedException {
|
||||
if (workerWrappers == null || workerWrappers.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
//保存上次执行的线程池变量(为了兼容以前的旧功能)
|
||||
Async.lastExecutorService = Objects.requireNonNull(executorService, "ExecutorService is null ! ");
|
||||
//定义一个map,存放所有的wrapper,key为wrapper的唯一id,value是该wrapper,可以从value中获取wrapper的result
|
||||
final ConcurrentMap<String, WorkerWrapper<?,?>> forParamUseWrappers =
|
||||
new ConcurrentHashMap<>(Math.max(workerWrappers.size() * 3, 8));
|
||||
final WrapperEndingInspector inspector = new WrapperEndingInspector(SystemClock.now() + timeout);
|
||||
inspector.addWrapper(workerWrappers);
|
||||
WorkerWrapperGroup group = new WorkerWrapperGroup(SystemClock.now(), timeout);
|
||||
group.addWrapper(workerWrappers);
|
||||
workerWrappers.forEach(wrapper -> {
|
||||
if (wrapper == null) {
|
||||
return;
|
||||
}
|
||||
executorService.submit(() -> wrapper.work(executorService, timeout, forParamUseWrappers, inspector));
|
||||
executorService.submit(() -> wrapper.work(executorService, timeout, group));
|
||||
});
|
||||
inspector.registerToPollingCenter();
|
||||
return inspector.await();
|
||||
return group.awaitFinish();
|
||||
//处理超时的逻辑被移动到了WrapperEndingInspector中。
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果想自定义线程池,请传pool。不自定义的话,就走默认的COMMON_POOL
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean beginWork(long timeout, ExecutorService executorService, WorkerWrapper... workerWrapper)
|
||||
throws ExecutionException, InterruptedException {
|
||||
if (workerWrapper == null || workerWrapper.length == 0) {
|
||||
@@ -138,6 +135,9 @@ public class Async {
|
||||
*/
|
||||
private static volatile ExecutorService lastExecutorService;
|
||||
|
||||
/**
|
||||
* 该方法将会返回{@link #COMMON_POOL},如果还未初始化则会懒加载初始化后再返回。
|
||||
*/
|
||||
public static ThreadPoolExecutor getCommonPool() {
|
||||
if (COMMON_POOL == null) {
|
||||
synchronized (Async.class) {
|
||||
@@ -181,7 +181,14 @@ public class Async {
|
||||
",largestCount=" + COMMON_POOL.getLargestPoolSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param now 是否立即关闭
|
||||
* @throws IllegalStateException 如果尚未调用过{@link #getCommonPool()},即没有使用过“使用默认线程池”的方法,该方法会抛出空指针异常。
|
||||
*/
|
||||
public static synchronized void shutDownCommonPool(boolean now) {
|
||||
if (COMMON_POOL == null) {
|
||||
throw new IllegalStateException("COMMON_POOL Not initialized yet");
|
||||
}
|
||||
if (!COMMON_POOL.isShutdown()) {
|
||||
if (now) {
|
||||
COMMON_POOL.shutdownNow();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jd.platform.async.executor;
|
||||
|
||||
import com.jd.platform.async.util.timer.Timeout;
|
||||
import com.jd.platform.async.util.timer.TimerTask;
|
||||
import com.jd.platform.async.util.timer.HashedWheelTimer;
|
||||
import com.jd.platform.async.util.timer.Timer;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperGroup;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 检查{@link WorkerWrapperGroup}是否调用完成的轮询中心。
|
||||
* <p>
|
||||
* <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 java.util.concurrent.CompletableFuture#allOf(CompletableFuture[])}等待B1与B2执行完成。
|
||||
* 线程2执行了B1或B2中的一个,也在allOf方法等待C1、C2完成。
|
||||
* 结果没有线程执行C和B2了,导致超时而死,并且这个线程池线程有可能被耗尽。
|
||||
* >
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午9:22
|
||||
*/
|
||||
public class PollingCenter {
|
||||
|
||||
// ========== singleton instance ==========
|
||||
|
||||
private static final PollingCenter instance = new PollingCenter();
|
||||
|
||||
public static PollingCenter getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ========== fields and methods ==========
|
||||
|
||||
public void checkGroup(WorkerWrapperGroup.CheckFinishTask task) {
|
||||
checkGroup(task, 0);
|
||||
}
|
||||
|
||||
public void checkGroup(WorkerWrapperGroup.CheckFinishTask task, long daley) {
|
||||
timer.newTimeout(task, daley, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private final Timer timer = new Timer() {
|
||||
private final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(
|
||||
r -> {
|
||||
Thread thread = new Thread(r, "asyncTool-pollingThread");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
},
|
||||
4,
|
||||
TimeUnit.MILLISECONDS,
|
||||
1024);
|
||||
|
||||
@Override
|
||||
public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
|
||||
return hashedWheelTimer.newTimeout(task, delay, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Timeout> stop() {
|
||||
return hashedWheelTimer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PollingCenter.timer";
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.jd.platform.async.util.collection;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* 一个反复循环的迭代器
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午6:25
|
||||
*/
|
||||
public interface WheelIterator<E> extends Iterator<E> {
|
||||
@Override
|
||||
E next();
|
||||
|
||||
/**
|
||||
* 一轮的元素数
|
||||
*/
|
||||
int cycle();
|
||||
|
||||
@Override
|
||||
default boolean hasNext() {
|
||||
return cycle() > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jd.platform.async.util.timer;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/12-下午6:36
|
||||
*/
|
||||
public abstract class AbstractWheelTimer implements Timer, AutoCloseable {
|
||||
public static final int WORKER_STATE_INIT = 0;
|
||||
public static final int WORKER_STATE_STARTED = 1;
|
||||
public static final int WORKER_STATE_SHUTDOWN = 2;
|
||||
|
||||
public abstract void start();
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
package com.jd.platform.async.util.timer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 从netty里抄来的,删去了一些功能。
|
||||
* <p/>
|
||||
* <b>
|
||||
* 如果违反开源协议,请联系作者: zh0u.he@qq.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.
|
||||
*/
|
||||
public HashedWheelTimer() {
|
||||
this(Executors.defaultThreadFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default thread factory
|
||||
* ({@link Executors#defaultThreadFactory()}) and default number of ticks
|
||||
* per wheel.
|
||||
*
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @throws NullPointerException if {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code tickDuration} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(long tickDuration, TimeUnit unit) {
|
||||
this(Executors.defaultThreadFactory(), tickDuration, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default thread factory
|
||||
* ({@link Executors#defaultThreadFactory()}).
|
||||
*
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @throws NullPointerException if {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) {
|
||||
this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default tick duration and default number of
|
||||
* ticks per wheel.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @throws NullPointerException if {@code threadFactory} is {@code null}
|
||||
*/
|
||||
public HashedWheelTimer(ThreadFactory threadFactory) {
|
||||
this(threadFactory, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer with the default number of ticks per wheel.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code tickDuration} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory, long tickDuration, TimeUnit unit) {
|
||||
this(threadFactory, tickDuration, unit, 512);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory,
|
||||
long tickDuration, TimeUnit unit, int ticksPerWheel) {
|
||||
this(threadFactory, tickDuration, unit, ticksPerWheel, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @param leakDetection {@code true} if leak detection should be enabled always,
|
||||
* if false it will only be enabled if the worker thread is not
|
||||
* a daemon thread.
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory,
|
||||
long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection) {
|
||||
this(threadFactory, tickDuration, unit, ticksPerWheel, leakDetection, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new timer.
|
||||
*
|
||||
* @param threadFactory a {@link ThreadFactory} that creates a
|
||||
* background {@link Thread} which is dedicated to
|
||||
* {@link TimerTask} execution.
|
||||
* @param tickDuration the duration between tick
|
||||
* @param unit the time unit of the {@code tickDuration}
|
||||
* @param ticksPerWheel the size of the wheel
|
||||
* @param leakDetection {@code true} if leak detection should be enabled always,
|
||||
* if false it will only be enabled if the worker thread is not
|
||||
* a daemon thread.
|
||||
* @param maxPendingTimeouts The maximum number of pending timeouts after which call to
|
||||
* {@code newTimeout} will result in
|
||||
* {@link java.util.concurrent.RejectedExecutionException}
|
||||
* being thrown. No maximum pending timeouts limit is assumed if
|
||||
* this value is 0 or negative.
|
||||
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
|
||||
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
|
||||
*/
|
||||
public HashedWheelTimer(
|
||||
ThreadFactory threadFactory,
|
||||
long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
|
||||
long maxPendingTimeouts) {
|
||||
|
||||
Objects.requireNonNull(threadFactory, "threadFactory must not null !");
|
||||
Objects.requireNonNull(threadFactory, "unit must not null !");
|
||||
if (tickDuration <= 0) {
|
||||
throw new IllegalArgumentException("tickDuration should > 0 !");
|
||||
}
|
||||
if (ticksPerWheel <= 0) {
|
||||
throw new IllegalArgumentException("ticksPerWheel should > 0 !");
|
||||
}
|
||||
|
||||
wheel = createWheel(ticksPerWheel);
|
||||
mask = wheel.length - 1;
|
||||
|
||||
long duration = unit.toNanos(tickDuration);
|
||||
|
||||
// 检查一个周期是否比Long.MAX_VALUE还长
|
||||
if (duration >= Long.MAX_VALUE / wheel.length) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
|
||||
tickDuration, Long.MAX_VALUE / wheel.length));
|
||||
}
|
||||
|
||||
this.tickDuration = Math.max(duration, MILLISECOND_NANOS);
|
||||
workerThread = threadFactory.newThread(worker);
|
||||
this.maxPendingTimeouts = maxPendingTimeouts;
|
||||
}
|
||||
|
||||
private static HashedWheelBucket[] createWheel(int ticksPerWheel) {
|
||||
if (ticksPerWheel <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"ticksPerWheel must be greater than 0: " + ticksPerWheel);
|
||||
}
|
||||
if (ticksPerWheel > 1073741824) {
|
||||
throw new IllegalArgumentException(
|
||||
"ticksPerWheel may not be greater than 2^30: " + ticksPerWheel);
|
||||
}
|
||||
|
||||
ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
|
||||
HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
|
||||
for (int i = 0; i < wheel.length; i++) {
|
||||
wheel[i] = new HashedWheelBucket();
|
||||
}
|
||||
return wheel;
|
||||
}
|
||||
|
||||
private static int normalizeTicksPerWheel(int ticksPerWheel) {
|
||||
int normalizedTicksPerWheel = 1;
|
||||
while (normalizedTicksPerWheel < ticksPerWheel) {
|
||||
normalizedTicksPerWheel <<= 1;
|
||||
}
|
||||
return normalizedTicksPerWheel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显式启动后台线程。即使您没有调用此方法,后台线程也将根据需要自动启动。
|
||||
*
|
||||
* @throws IllegalStateException 如果此计时器已停止{@link #stop()}
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
switch (workerState.get()) {
|
||||
case WORKER_STATE_INIT:
|
||||
if (workerState.compareAndSet(WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
|
||||
workerThread.start();
|
||||
}
|
||||
break;
|
||||
case WORKER_STATE_STARTED:
|
||||
break;
|
||||
case WORKER_STATE_SHUTDOWN:
|
||||
throw new IllegalStateException("cannot be started once stopped");
|
||||
default:
|
||||
throw new Error("Invalid WorkerState");
|
||||
}
|
||||
|
||||
// Wait until the startTime is initialized by the worker.
|
||||
while (startTime == 0) {
|
||||
try {
|
||||
startTimeInitialized.await();
|
||||
} catch (InterruptedException ignore) {
|
||||
// Ignore - it will be ready very soon.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/jd/platform/async/util/timer/Timeout.java
Normal file
36
src/main/java/com/jd/platform/async/util/timer/Timeout.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.jd.platform.async.util.timer;
|
||||
|
||||
/**
|
||||
* 借鉴netty。
|
||||
* 一个连接着{@link Timer}和{@link TimerTask},表示着任务状态的“关系类”
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午6:33
|
||||
*/
|
||||
public interface Timeout {
|
||||
/**
|
||||
* 返回对应的{@link Timer}。
|
||||
*/
|
||||
Timer timer();
|
||||
|
||||
/**
|
||||
* 返回对应的{@link TimerTask}
|
||||
*/
|
||||
TimerTask task();
|
||||
|
||||
/**
|
||||
* 当且仅当关联的{@link TimerTask}已超时时,才返回{@code true}。
|
||||
*/
|
||||
boolean isExpired();
|
||||
|
||||
/**
|
||||
* 当且仅当关联的{@link TimerTask}被取消时,才返回{@code true}。
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* 尝试取消关联的{@link TimerTask}。如果任务已经执行或已取消,它将无副作用地返回。
|
||||
*
|
||||
* @return 如果取消成功完成,则为true,否则为false
|
||||
*/
|
||||
boolean cancel();
|
||||
}
|
||||
41
src/main/java/com/jd/platform/async/util/timer/Timer.java
Normal file
41
src/main/java/com/jd/platform/async/util/timer/Timer.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.jd.platform.async.util.timer;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 照抄netty
|
||||
* 让{@link TimerTask}在后台线程中执行。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午6:33
|
||||
*/
|
||||
public interface Timer {
|
||||
|
||||
/**
|
||||
* 使{@link TimerTask}在指定的延迟后执行一次。
|
||||
*
|
||||
* @param delay 延时长度
|
||||
* @param unit 延时单位
|
||||
* @return 返回 {@link Timeout}关系类
|
||||
* @throws IllegalStateException 如果此计时器已经已停止
|
||||
* @throws RejectedExecutionException 如果挂起的超时太多,则创建新的超时会导致系统不稳定。
|
||||
*/
|
||||
Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);
|
||||
|
||||
default Timeout newTimeout(Runnable runnable, long delay, TimeUnit unit) {
|
||||
AtomicReference<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.util.timer;
|
||||
|
||||
/**
|
||||
* 类似于netty的TimerTask。
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-下午5:17
|
||||
*/
|
||||
public interface TimerTask{
|
||||
void run(Timeout timeout) throws Exception;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.jd.platform.async.worker;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 执行结果
|
||||
*/
|
||||
@@ -7,12 +9,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 +26,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 +48,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.jd.platform.async.wrapper;
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.*;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependMustStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependWrapperActionStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependWrapperStrategyMapper;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -93,6 +96,9 @@ class StableWorkerWrapperBuilder<T, V, BUILDER_SUB_CLASS extends StableWorkerWra
|
||||
private boolean enableTimeOut = false;
|
||||
private long time = -1;
|
||||
private TimeUnit unit = null;
|
||||
/**
|
||||
* 是否允许被打断
|
||||
*/
|
||||
private boolean allowInterrupt = false;
|
||||
|
||||
/**
|
||||
@@ -259,33 +265,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<>());
|
||||
@@ -329,8 +350,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) -> {
|
||||
DependWrapperStrategyMapper dependWrapperStrategyMapper = next.getWrapperStrategy().getDependWrapperStrategyMapper();
|
||||
if (dependWrapperStrategyMapper == null) {
|
||||
next.getWrapperStrategy().setDependWrapperStrategyMapper(
|
||||
dependWrapperStrategyMapper = new DependWrapperStrategyMapper());
|
||||
}
|
||||
dependWrapperStrategyMapper.putMapping(wrapper, strategy);
|
||||
});
|
||||
}
|
||||
}
|
||||
// ========== 设置检查是否跳过策略 ==========
|
||||
@@ -344,18 +371,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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,9 @@ package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.callback.IWorker;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependWrapperActionStrategy;
|
||||
import com.jd.platform.async.wrapper.actionstrategy.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.skipstrategy.SkipStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependWrapperActionStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.skip.SkipStrategy;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -257,13 +256,6 @@ public interface WorkerWrapperBuilder<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 +266,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,155 @@
|
||||
package com.jd.platform.async.wrapper;
|
||||
|
||||
import com.jd.platform.async.executor.PollingCenter;
|
||||
import com.jd.platform.async.util.timer.Timeout;
|
||||
import com.jd.platform.async.util.timer.TimerTask;
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/9-下午7:21
|
||||
*/
|
||||
public class WorkerWrapperGroup {
|
||||
/**
|
||||
* 任务开始时间
|
||||
*/
|
||||
private final long groupStartTime;
|
||||
/**
|
||||
* 任务限时
|
||||
*/
|
||||
private final long timeoutLength;
|
||||
/**
|
||||
* 该map存放所有wrapper的id和wrapper映射
|
||||
* <p/>
|
||||
* 需要线程安全。
|
||||
*/
|
||||
private final Map<String, WorkerWrapper<?, ?>> forParamUseWrappers = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 当全部wrapper都调用结束,它会countDown
|
||||
*/
|
||||
private final CountDownLatch endCDL = new CountDownLatch(1);
|
||||
|
||||
private final AtomicBoolean anyTimeout = new AtomicBoolean(false);
|
||||
|
||||
public WorkerWrapperGroup(long groupStartTime, long timeoutLength) {
|
||||
this.groupStartTime = groupStartTime;
|
||||
this.timeoutLength = timeoutLength;
|
||||
}
|
||||
|
||||
public void addWrapper(Collection<? 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步等待这组wrapper执行完成
|
||||
*
|
||||
* @return false代表有wrapper超时了。true代表全部wrapper没有超时。
|
||||
*/
|
||||
public boolean awaitFinish() throws InterruptedException {
|
||||
endCDL.await();
|
||||
return !anyTimeout.get();
|
||||
}
|
||||
|
||||
public class CheckFinishTask implements TimerTask {
|
||||
|
||||
@Override
|
||||
public void run(Timeout timeout) throws Exception {
|
||||
// 已经完成了
|
||||
if (endCDL.getCount() < 1) {
|
||||
return;
|
||||
}
|
||||
AtomicBoolean hasTimeout = new AtomicBoolean(false);
|
||||
// 记录正在运行中的wrapper里,最近的限时时间。
|
||||
AtomicLong minDaley = new AtomicLong(Long.MAX_VALUE);
|
||||
final Collection<WorkerWrapper<?, ?>> values = forParamUseWrappers.values();
|
||||
final Stream<WorkerWrapper<?, ?>> stream = values.size() > 1024 ? values.parallelStream() : values.stream();
|
||||
boolean allFinish = stream
|
||||
// 处理超时
|
||||
.peek(wrapper -> {
|
||||
// time_diff :
|
||||
// -1 -> already timeout ;
|
||||
// 0 -> finish but not timeout ;
|
||||
// X>0 -> is running , may timeout in X seconds .
|
||||
long time_diff = wrapper.checkTimeout(true, groupStartTime, timeoutLength);
|
||||
if (time_diff < 0) {
|
||||
hasTimeout.set(true);
|
||||
}
|
||||
if (time_diff == 0) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
long getMinDaley = minDaley.get();
|
||||
if (getMinDaley <= time_diff || minDaley.compareAndSet(getMinDaley, time_diff)) {
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
})
|
||||
// 判断是否结束,这里如果还有未结束的wrapper则会提前结束流。
|
||||
.allMatch(wrapper -> wrapper.getState().finished());
|
||||
long getMinDaley = minDaley.get();
|
||||
// 如果有正在运行的wrapper
|
||||
if (!allFinish) {
|
||||
// 如果有正在WORKING的wrapper,则计算一下限时时间,限时完成后轮询它。
|
||||
if (getMinDaley != Long.MAX_VALUE) {
|
||||
PollingCenter.getInstance().checkGroup(this, getMinDaley);
|
||||
}
|
||||
}
|
||||
if (allFinish) {
|
||||
anyTimeout.set(hasTimeout.get());
|
||||
endCDL.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
// hashCode and equals will called WorkerWrapperGroup.this
|
||||
|
||||
/**
|
||||
* 将会调用{@link WorkerWrapperGroup#hashCode()}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return WorkerWrapperGroup.this.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将会调用{@link WorkerWrapperGroup#equals(Object)}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof WorkerWrapperGroup.CheckFinishTask)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(WorkerWrapperGroup.this, ((CheckFinishTask) obj).getParent());
|
||||
}
|
||||
|
||||
private WorkerWrapperGroup getParent() {
|
||||
return WorkerWrapperGroup.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,6 +1,5 @@
|
||||
package com.jd.platform.async.wrapper.actionstrategy;
|
||||
package com.jd.platform.async.wrapper.strategy.depend;
|
||||
|
||||
import com.jd.platform.async.wrapper.WrapperEndingInspector;
|
||||
import com.jd.platform.async.worker.ResultState;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
@@ -41,7 +40,7 @@ public interface DependenceStrategy {
|
||||
* </p>
|
||||
* @return 返回枚举值内部类,WorkerWrapper将会根据其值来决定自己如何响应这次调用。 {@link DependenceAction.WithProperty}
|
||||
*/
|
||||
DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?,?>> dependWrappers,
|
||||
DependenceAction.WithProperty judgeAction(Set<WorkerWrapper<?, ?>> dependWrappers,
|
||||
WorkerWrapper<?, ?> thisWrapper,
|
||||
WorkerWrapper<?, ?> fromWrapper);
|
||||
|
||||
@@ -55,7 +54,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 +80,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 +118,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;
|
||||
@@ -159,7 +158,7 @@ public interface DependenceStrategy {
|
||||
*/
|
||||
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) {
|
||||
@@ -188,7 +187,7 @@ public interface DependenceStrategy {
|
||||
* @param theseWrapper 该方法唯一有效参数。
|
||||
* @return 返回生成的 {@link DependenceAction.WithProperty)
|
||||
*/
|
||||
static DependenceStrategy theseWrapperAllSuccess(Set<WorkerWrapper<?,?>> theseWrapper) {
|
||||
static DependenceStrategy theseWrapperAllSuccess(Set<WorkerWrapper<?, ?>> theseWrapper) {
|
||||
return new DependenceStrategy() {
|
||||
private final Set<WorkerWrapper<?, ?>> theseWrappers;
|
||||
private final String toString;
|
||||
@@ -199,7 +198,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 +231,16 @@ public interface DependenceStrategy {
|
||||
};
|
||||
}
|
||||
|
||||
DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() {
|
||||
/**
|
||||
* 此值用于适配v1.4及之前的must开关模式,
|
||||
* 当`wrapperStrategy`的`dependMustStrategyMapper`的`mustDependSet`不为空时,
|
||||
* 则休息(因为能判断到这个责任链说明set中存在不满足的值L)。
|
||||
* 为空时,则任一成功则执行。
|
||||
*
|
||||
* @deprecated 不推荐使用must开关
|
||||
*/
|
||||
@Deprecated
|
||||
DependenceStrategy IF_MUST_SET_NOT_EMPTY_ALL_SUCCESS_ELSE_ANY = new DependenceStrategy() {
|
||||
@Override
|
||||
public DependenceAction.WithProperty judgeAction(Set<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.*;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
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 +28,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 +44,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) {
|
||||
|
||||
62
src/test/java/v15/cases/Case1.java
Normal file
62
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.beginWork(1000, a, d);
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=D) is working
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=E) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=C) is working
|
||||
wrapper(id=G) is working
|
||||
wrapper(id=F) is working
|
||||
wrapper(id=H) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
53
src/test/java/v15/cases/Case2.java
Normal file
53
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.beginWork(20,wrapper100,wrapper200);
|
||||
System.out.println(add.getWorkResult());
|
||||
// 输出WorkResult{result=300, resultState=SUCCESS, ex=null}
|
||||
}
|
||||
}
|
||||
61
src/test/java/v15/cases/Case3.java
Normal file
61
src/test/java/v15/cases/Case3.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 示例:设置依赖策略--快速上手
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/8-下午10:58
|
||||
*/
|
||||
class Case3 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<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.beginWork(1000, pool, a);
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B3) is working
|
||||
wrapper(id=B1) is working
|
||||
wrapper(id=B2) is working
|
||||
wrapper(id=C2) is working
|
||||
wrapper(id=C1) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
69
src/test/java/v15/cases/Case4.java
Normal file
69
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.beginWork(1000, pool, a);
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B2) is working
|
||||
wrapper(id=B1) is working
|
||||
wrapper(id=B4) is working
|
||||
wrapper(id=B3) is working
|
||||
wrapper(id=B5) is working
|
||||
wrapper(id=C) is working
|
||||
由于B1-B10是并行的,所以正好仅有3个wrapper成功,在多线程环境中是比较难遇到的。
|
||||
*/
|
||||
}
|
||||
}
|
||||
75
src/test/java/v15/cases/Case5.java
Normal file
75
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.beginWork(1000, pool, start);
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
/* 输出:
|
||||
wrapper(id=A1) is working
|
||||
wrapper(id=A2) is working
|
||||
wrapper(id=A4) is working
|
||||
wrapper(id=A3) is working
|
||||
wrapper(id=A5) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=A6) is working
|
||||
我们可以看到,A3、A4执行后,B也执行了,之后的wrapper被跳过了
|
||||
(这里之所以a5、a6还在执行,只是因为他两正好在WORKING,所以没发现后面的B已经可以跳过了)
|
||||
*/
|
||||
}
|
||||
}
|
||||
61
src/test/java/v15/cases/Case6.java
Normal file
61
src/test/java/v15/cases/Case6.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.DependWrapperActionStrategy;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceAction;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 示例:自定义依赖策略--对单个wrapper设置“上克下”策略--简单使用与示例
|
||||
*
|
||||
* @author create by TcSnZh on 2021/5/9-上午1:42
|
||||
*/
|
||||
class Case6 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return WorkerWrapper.<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<?, ?> b = builder("B")
|
||||
// 这里设置了,不论a怎么样b都会快速失败。但是,a设置的对wrapper的特殊策略把它覆盖了。
|
||||
.depends((dependWrappers, thisWrapper, fromWrapper) ->
|
||||
DependenceAction.FAST_FAIL
|
||||
.fastFailException(ResultState.EXCEPTION, new RuntimeException("b 必定失败,除非有上游wrapper救他"))
|
||||
)
|
||||
.build();
|
||||
WorkerWrapper<?, ?> a = builder("A")
|
||||
.setNext()
|
||||
// a将会使b直接开始工作
|
||||
// 若是去掉这行代码,则b会失败
|
||||
.specialToNextWrapper(fromWrapper -> DependenceAction.START_WORK.emptyProperty(), b)
|
||||
.wrapper(b)
|
||||
.end().build();
|
||||
Async.beginWork(1000, a);
|
||||
System.out.println(a.getWorkResult());
|
||||
System.out.println(b.getWorkResult());
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=B) is working
|
||||
WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
*/
|
||||
}
|
||||
}
|
||||
61
src/test/java/v15/cases/Case7.java
Normal file
61
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.beginWork(1000, a);
|
||||
/* 输出:
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=E) is working
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=D) is working
|
||||
*/
|
||||
}
|
||||
}
|
||||
73
src/test/java/v15/cases/Case8.java
Normal file
73
src/test/java/v15/cases/Case8.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package v15.cases;
|
||||
|
||||
import com.jd.platform.async.callback.ICallback;
|
||||
import com.jd.platform.async.executor.Async;
|
||||
import com.jd.platform.async.worker.WorkResult;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapper;
|
||||
import com.jd.platform.async.wrapper.WorkerWrapperBuilder;
|
||||
import com.jd.platform.async.wrapper.strategy.depend.DependenceStrategy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* @author create by TcSnZh on 2021/5/9-下午4:34
|
||||
*/
|
||||
class Case8 {
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id) {
|
||||
return builder(id, -1L);
|
||||
}
|
||||
|
||||
private static WorkerWrapperBuilder<?, ?> builder(String id, long sleepTime) {
|
||||
return WorkerWrapper.<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.beginWork(15, a);
|
||||
/* 输出:
|
||||
wrapper(id=A) has begin .
|
||||
wrapper(id=A) is working
|
||||
wrapper(id=A) callback success , workResult is WorkResult{result=null, resultState=SUCCESS, ex=null}
|
||||
wrapper(id=B) has begin .
|
||||
wrapper(id=B) is working
|
||||
wrapper(id=B) callback success , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null}
|
||||
wrapper(id=C) has begin .
|
||||
wrapper(id=C) callback fail , workResult is WorkResult{result=null, resultState=TIMEOUT, ex=null}
|
||||
java.lang.InterruptedException: sleep interrupted
|
||||
at java.lang.Thread.sleep(Native Method)
|
||||
以下异常信息省略
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user