All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.iohao.game.common.kit.concurrent.TaskKit Maven / Gradle / Ivy

/*
 * ioGame
 * Copyright (C) 2021 - present  渔民小镇 ([email protected][email protected]) . All Rights Reserved.
 * # iohao.com . 渔民小镇
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package com.iohao.game.common.kit.concurrent;

import com.iohao.game.common.kit.CollKit;
import com.iohao.game.common.kit.ExecutorKit;
import com.iohao.game.common.kit.collect.SetMultiMap;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import lombok.Getter;
import lombok.experimental.UtilityClass;

import java.util.Objects;
import java.util.Set;
import java.util.concurrent.*;
import java.util.function.Supplier;

/**
 * 任务消费相关的内部工具类,开发者不要用在耗时 io 的任务上
 * 

* example - 使用其他线程执行任务 *

{@code
 *     TaskKit.execute(()->{
 *         log.info("你的逻辑");
 *     });
 * }
 * 
* example - netty TimerTask *
{@code
 *     // 3 秒后执行
 *     TaskKit.newTimeout(new TimerTask() {
 *         @Override
 *         public void run(Timeout timeout) {
 *             log.info("3-newTimeout : {}", timeout);
 *         }
 *     }, 3, TimeUnit.SECONDS);
 * }
 * 
* example - TaskListener 监听回调。内部使用 HashedWheelTimer 来模拟 ScheduledExecutorService 调度 *
{@code
 *     // 只执行一次,2 秒后执行
 *     TaskKit.runOnce(() -> log.info("2 Seconds"), 2, TimeUnit.SECONDS);
 *     // 只执行一次,1 分钟后执行
 *     TaskKit.runOnce(() -> log.info("1 Minute"), 1, TimeUnit.MINUTES)
 *     // 只执行一次,500、800 milliseconds 后
 *     TaskKit.runOnce(() -> log.info("500 delayMilliseconds"), 500);
 *     TaskKit.runOnce(() -> log.info("800 delayMilliseconds"), 800);
 *
 *     // 每分钟调用一次
 *     TaskKit.runIntervalMinute(() -> log.info("tick 1 Minute"), 1);
 *     // 每 2 分钟调用一次
 *     TaskKit.runIntervalMinute(() -> log.info("tick 2 Minute"), 2);
 *
 *     // 每 2 秒调用一次
 *     TaskKit.runInterval(() -> log.info("tick 2 Seconds"), 2, TimeUnit.SECONDS);
 *     // 每 30 分钟调用一次
 *     TaskKit.runInterval(() -> log.info("tick 30 Minute"), 30, TimeUnit.MINUTES);
 * }
 * 
* example - TaskListener - 高级用法 *
{@code
 *      //【示例 - 移除任务】每秒调用一次,当 hp 为 0 时就移除当前 Listener
 *     TaskKit.runInterval(new IntervalTaskListener() {
 *         int hp = 2;
 *
 *         @Override
 *         public void onUpdate() {
 *             hp--;
 *             log.info("剩余 hp:2-{}", hp);
 *         }
 *
 *         @Override
 *         public boolean isActive() {
 *             // 当返回 false 则表示不活跃,会从监听列表中移除当前 Listener
 *             return hp != 0;
 *         }
 *     }, 1, TimeUnit.SECONDS);
 *
 *     //【示例 - 跳过执行】每秒调用一次,当 triggerUpdate 返回值为 true,即符合条件时才执行 onUpdate 方法
 *     TaskKit.runInterval(new IntervalTaskListener() {
 *         int hp;
 *
 *         @Override
 *         public void onUpdate() {
 *             log.info("current hp:{}", hp);
 *         }
 *
 *         @Override
 *         public boolean triggerUpdate() {
 *             hp++;
 *             // 当返回值为 true 时,会执行 onUpdate 方法
 *             return hp % 2 == 0;
 *         }
 *     }, 1, TimeUnit.SECONDS);
 *
 *     //【示例 - 指定线程执行器】每秒调用一次
 *     // 如果有耗时的任务,比如涉及一些 io 操作的,建议指定执行器来执行当前回调(onUpdate 方法),以避免阻塞其他任务。
 *     TaskKit.runInterval(new IntervalTaskListener() {
 *         @Override
 *         public void onUpdate() {
 *             log.info("执行耗时的 IO 任务,开始");
 *
 *             try {
 *                 TimeUnit.SECONDS.sleep(3);
 *             } catch (InterruptedException e) {
 *                 throw new RuntimeException(e);
 *             }
 *
 *             log.info("执行耗时的 IO 任务,结束");
 *         }
 *
 *         @Override
 *         public Executor getExecutor() {
 *             // 指定执行器来执行当前回调(onUpdate 方法),以避免阻塞其他任务。
 *             return TaskKit.getCacheExecutor();
 *         }
 *     }, 1, TimeUnit.SECONDS);
 * }
 * 
* * @author 渔民小镇 * @date 2023-12-02 */ @UtilityClass public class TaskKit { /** 执行一些没有 io 操作的逻辑 */ private final HashedWheelTimer wheelTimer = new HashedWheelTimer(); /** 内置的 cacheExecutor 执行器 */ @Getter final ExecutorService cacheExecutor = ExecutorKit.newCacheThreadPool("ioGameThread-"); /** 虚拟线程执行器 */ @Getter final ExecutorService virtualExecutor = ExecutorKit.newVirtualExecutor("ioGameVirtual-"); final SetMultiMap intervalTaskListenerMap = SetMultiMap.of(); record TickTimeUnit(long tick, TimeUnit timeUnit) { } /** * 使用其他线程执行任务 * * @param command 任务 */ public void execute(Runnable command) { cacheExecutor.execute(command); } /** * 使用虚拟线程执行任务 * * @param command 任务 */ public void executeVirtual(Runnable command) { virtualExecutor.execute(command); } /** * 返回一个 CompletableFuture,该任务会在 virtualExecutor(虚拟线程) 中异步运行,结果将从 Supplier 中获得 * * @param supplier supplier * @param u * @return CompletableFuture * @see TaskKit#virtualExecutor */ public CompletableFuture supplyAsync(Supplier supplier) { return CompletableFuture.supplyAsync(supplier, virtualExecutor); } /** * 延迟一定时间后执行任务; * * @param task 任务 * @param delay 延迟时间 * @param unit 延迟时间单位 * @return Timeout */ public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) { return wheelTimer.newTimeout(task, delay, unit); } /** * 添加 OnceTaskListener 监听回调,只会执行一次 * * @param taskListener taskListener * @param delay 延迟时间 * @param unit 延迟时间单位 */ public void runOnce(OnceTaskListener taskListener, long delay, TimeUnit unit) { newTimeout(taskListener, delay, unit); } /** * 一秒后执行 OnceTaskListener 监听回调,只会执行一次 * * @param taskListener taskListener */ public void runOnceSecond(OnceTaskListener taskListener) { runOnce(taskListener, 1, TimeUnit.SECONDS); } /** * 添加 OnceTaskListener 监听回调,只会执行一次 * * @param taskListener taskListener * @param delayMilliseconds delayMilliseconds */ public void runOnceMillis(OnceTaskListener taskListener, long delayMilliseconds) { runOnce(taskListener, delayMilliseconds, TimeUnit.MILLISECONDS); } /** * 添加调度任务监听 * * @param taskListener 调度任务监听 * @param tickMinute 每 tickMinute 分钟,会调用一次监听 */ public void runIntervalMinute(IntervalTaskListener taskListener, long tickMinute) { runInterval(taskListener, tickMinute, TimeUnit.MINUTES); } /** * 添加任务监听回调 *
     *     每 tick 时间单位,会调用一次任务监听
     * 
* * @param taskListener 任务监听 * @param tick tick 时间间隔;每 tick 时间间隔,会调用一次监听 * @param timeUnit tick 时间单位 */ public void runInterval(IntervalTaskListener taskListener, long tick, TimeUnit timeUnit) { TickTimeUnit tickTimeUnit = new TickTimeUnit(tick, timeUnit); Set intervalTaskListeners = intervalTaskListenerMap.get(tickTimeUnit); // 无锁化 if (CollKit.isEmpty(intervalTaskListeners)) { intervalTaskListeners = intervalTaskListenerMap.ofIfAbsent(tickTimeUnit, (initSet) -> { // 使用 HashedWheelTimer 来模拟 ScheduledExecutorService 调度 foreverTimerTask(tick, timeUnit, initSet); }); } intervalTaskListeners.add(taskListener); } private void foreverTimerTask(long tick, TimeUnit timeUnit, Set set) { // 启动定时器 TaskKit.newTimeout(new TimerTask() { @Override public void run(Timeout timeout) { if (set.isEmpty()) { TaskKit.newTimeout(this, tick, timeUnit); return; } set.forEach(intervalTaskListener -> { var executor = intervalTaskListener.getExecutor(); // 如果指定了执行器,就将执行流程放到执行器中,否则使用当前线程 if (Objects.nonNull(executor)) { executor.execute(() -> executeFlowTimerListener(intervalTaskListener, set)); } else { executeFlowTimerListener(intervalTaskListener, set); } }); TaskKit.newTimeout(this, tick, timeUnit); } }, tick, timeUnit); } private void executeFlowTimerListener(IntervalTaskListener taskListener, Set set) { try { // 移除不活跃的监听 if (!taskListener.isActive()) { set.remove(taskListener); return; } if (taskListener.triggerUpdate()) { taskListener.onUpdate(); } } catch (Throwable e) { taskListener.onException(e); } } public void stop() { wheelTimer.stop(); cacheExecutor.shutdown(); virtualExecutor.shutdown(); } }