SpringBoot中使用单例模式+ScheduledExecutorService实现异步多线程任务(若依源码学习)

场景

若依前后端分离版手把手教你本地搭建环境并运行项目:

若依前后端分离版手把手教你本地搭建环境并运行项目_本地运行若依前后端分离-CSDN博客

设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例:

设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例_静态类 java 饿汉-CSDN博客

Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例:

Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例_threadpoolexecutor创建线程-CSDN博客

结合以上,学习并模仿若依登录接口中异步记录登录日志的操作。

在若依登录的SysLoginService中login登录方式有异步记录登录日志的操作。

其中核心代码为:

 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));

代码位置

注:

博客:
霸道流氓气质-CSDN博客

实现

1、先看下若依自己本身的实现

AsyncManager异步任务管理器实现


/**
 * 异步任务管理器
 *
 *
 */
public class AsyncManager {
    private static AsyncManager me = new AsyncManager();
    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;
    /**
     * 异步操作任务调度线程池
     */
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    /**
     * 单例模式
     */
    private AsyncManager() {
    }

    public static AsyncManager me() {
        return me;
    }

    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(TimerTask task) {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止任务线程池
     */
    public void shutdown() {
        Threads.shutdownAndAwaitTermination(executor);
    }
}

syncManager.me()获取一个AsyncManager对象。

执行execute方法,执行任务,传入的是一个task对象,实现了Runnable接口,是一个任务,由线程Thread去执行。

用到了单例模式,具体可参考上面。

task对象通过异步工厂类创建

public class AsyncFactory {
    private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");

    /**
     * 记录登录信息
     *
     * @param username 用户名
     * @param status   状态
     * @param message  消息
     * @param args     列表
     * @return 任务task
     */
    public static TimerTask recordLogininfor(final String username, final String status, final String message,
                                             final Object... args) {
        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        return new TimerTask() {
            @Override
            public void run() {
                String address = AddressUtils.getRealAddressByIP(ip);
                StringBuilder s = new StringBuilder();
                s.append(LogUtils.getBlock(ip));
                s.append(address);
                s.append(LogUtils.getBlock(username));
                s.append(LogUtils.getBlock(status));
                s.append(LogUtils.getBlock(message));
                // 打印信息到日志
                sys_user_logger.info(s.toString(), args);
                // 获取客户端操作系统
                String os = userAgent.getOperatingSystem().getName();
                // 获取客户端浏览器
                String browser = userAgent.getBrowser().getName();
                // 封装对象
                SysLogininfor logininfor = new SysLogininfor();
                logininfor.setUserName(username);
                logininfor.setIpaddr(ip);
                logininfor.setLoginLocation(address);
                logininfor.setBrowser(browser);
                logininfor.setOs(os);
                logininfor.setMsg(message);
                // 日志状态
                if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
                    logininfor.setStatus(Constants.SUCCESS);
                } else if (Constants.LOGIN_FAIL.equals(status)) {
                    logininfor.setStatus(Constants.FAIL);
                }
                // 插入数据
                SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
            }
        };
    }

    /**
     * 操作日志记录
     *
     * @param operLog 操作日志信息
     * @return 任务task
     */
    public static TimerTask recordOper(final SysOperLog operLog) {
        return new TimerTask() {
            @Override
            public void run() {
                // 远程查询操作地点
                operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
                SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
            }
        };
    }
}

2、下面模仿上面的实现方式,去除掉无用的业务代码,对流程进行简化。

新建全局异步任务管理器

import com.ruoyi.common.utils.Threads;
import com.ruoyi.common.utils.spring.SpringUtils;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 全局异步任务管理器
 */
public class GlobalAsyncManager {

    //单例
    private static final GlobalAsyncManager instance = new GlobalAsyncManager();

    //延迟执行时间
    private final int OPERATOR_DELAY_TIME = 10;

    private ScheduledExecutorService executorService = SpringUtils.getBean("scheduledExecutorService");

    private GlobalAsyncManager(){

    }

    public static GlobalAsyncManager getInstance(){
        return  instance;
    }

    //执行任务
    public void executorTask(TimerTask task){
        executorService.schedule(task,OPERATOR_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

    //停止任务线程池
    public void shutdown(){
        Threads.shutdownAndAwaitTermination(executorService);
    }
}

这里创建ScheduledExecutorService是使用的若依封装的spring工具类

停止任务线程池也是用的若依的线程相关工具类

新建异步工厂,产生任务用

import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.BusStudent;
import com.ruoyi.system.service.IBusStudentService;
import java.util.TimerTask;

/**
 * 异步工厂,产生任务用
 */
public class AsyncTaskFactory {
    public static TimerTask recordLogToData(String logText){
        return new TimerTask() {
            @Override
            public void run() {
                BusStudent busStudent = new BusStudent();
                busStudent.setAddress(logText);
                SpringUtils.getBean(IBusStudentService.class).insertBusStudent(busStudent);
            }
        };
    }
}

这里模拟插入日志到数据库中,数据库的表这里随便找了一个表。

调用示例

    @Test
    public void recordLog() throws InterruptedException {
        Thread.sleep(1000);
        GlobalAsyncManager.getInstance().executorTask(AsyncTaskFactory.recordLogToData(LocalDateTime.now()+"日志内容"));
        Thread.sleep(3000);
        System.out.println(LocalDateTime.now());
    }

为了验证其是异步效果,这里模拟线程休眠并记录时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/309353.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android SDK环境搭建[图解]; 解决问题Done. Nothing was installed.

安装SDK Android SDK环境搭建 依赖java环境,需要自备Java环境 (100%实操成功) 目录 1. 解压:解压到非中文无特殊字符的目录 2. 双击:SDK Manager.exe,不要选全部!不要选全部!不要选全部!(会下很久) 3. 然后勾选组件​ 4. 设置环境变量 …

安装ubuntu22.04系统,GPU驱动,cuda,cudnn,python环境,pycharm

需要准备一个u盘,需要格式化,且内存不小于8g 1 下载ubuntu镜像 下载链接: https://cn.ubuntu.com/download/desktop 2下载rufus Rufus - 轻松创建 USB 启动盘Rufus: Create bootable USB drives the easy wayhttps://rufus.ie/zh/ 准备好这…

用Linux的视角来理解缓冲区概念

缓冲区的认识 缓冲区(buffer)是存储数据的临时存储区域。当我们用C语言向文件中写入数据时,数据并不会直接的写到文件中,中途还经过了缓冲区,而我们需要对缓冲区的数据进行刷新,那么数据才算写到文件当中。…

STL之list

目录 list定义和结构 list容器模板接受两个参数: list容器的特点 双向性 动态大小 不连续存储 实例 代码输出 需要注意的点 list常用函数 代码示例 list定义和结构 list的使用频率不高,在做题时极少遇到需要使用list的情景。 list是一种双向…

LeetCode 232.用栈实现队列(详解) (๑•̌.•๑)

题目描述: 解题思路: 创建两个栈,一个用于入数据,一个用于出数据。分别是pushST和popST; 1.如果是入数据就直接入进pushST 2.如果是出数据,先检查popST中有无数据,如果有数据,就直接出。如果没…

Xcode15 升级问题记录

这里写自定义目录标题 新版本Xcode15升级问题1:rsync error: some files could not be transferred (code 23) at ...参考 新版本Xcode15升级 下载地址:https://developer.apple.com/download/all/ 我目前使用的版本是Xcode15.2 我新创建了一个项目&…

建立四叉树[中等]

一、题目 给你一个n * n矩阵grid,矩阵由若干0和1组成。请你用四叉树表示该矩阵grid。你需要返回能表示矩阵grid的四叉树的根结点。四叉树数据结构中,每个内部节点只有四个子节点。此外,每个节点都有两个属性: 【1】val&#xff1…

使用SpringCache操作Redis缓存数据

SpringCache概念 SpringCache是一个框架,实现了基于注解的缓存功能,只需要简单的加一个注解,就能实现缓存功能。 SpringCache提供了一层抽象,底层可以切换不同的缓存实现,例如: EHCacheCaffeineRedis 使…

怎么在unity3D工程中导入Newtonsoft.Json

打开 Unity 编辑器。 转到菜单栏的 “Window”(窗口)选项,然后选择 “Package Manager”(包管理器) 在搜索框中输入 “Newtonsoft Json” 进行搜索。 注意:要选择Unity Registry 在搜索结果中&#xf…

强化学习求解TSP(五):Qlearning求解旅行商问题TSP(提供Python代码)

一、Qlearning简介 Q-learning是一种强化学习算法,用于解决基于奖励的决策问题。它是一种无模型的学习方法,通过与环境的交互来学习最优策略。Q-learning的核心思想是通过学习一个Q值函数来指导决策,该函数表示在给定状态下采取某个动作所获…

Windows下安装mariadb10.5数据库及配置详细教程

1、简介 MariaDB数据库管理系统是一款MySQL的替代数据库。MariaDB由MySQL的创始人麦克尔维德纽斯主导开发,是可扩展的,可靠的SQL服务器的合乎逻辑的选择,MariaDB 10.5 是 MariaDB 当前的稳定系列。 2、下载 下载地址:Download M…

【FPGA/verilog -入门学习17】vivado 实现串口自发自收程序

1,需求 PC使用串口助手给FPGA板发送9600 波特率的数据,FPGA板接收到数据后,回复同样的数据给PC 2,需求分析 按模块可以划分为: rx接收模块,将输入的8位并行rx 数据转换成[7:0]rx_data 信号,当…

圣诞老人遇见 GenAI:利用大语言模型、LangChain 和 Elasticsearch 破译手写的圣诞信件

在北极的中心地带,圣诞老人的精灵团队面临着巨大的后勤挑战:如何处理来自世界各地儿童的数百万封信件。 圣诞老人表情坚定,他决定是时候将人工智能纳入圣诞节行动了。 圣诞老人坐在配备了最新人工智能技术的电脑前,开始在 Jupyter…

【Linux】Ubuntu 解压 zip、z01、z02等压缩文件的方法,Linux如何解压分卷压缩的

zip分卷压缩,在windows上压缩来的,如何解压这种文件: -rw-rw-r-- 1 20401094656 Dec 10 20:06 FFHQ.z01 -rw-rw-r-- 1 20401094656 Dec 10 20:10 FFHQ.z02 -rw-rw-r-- 1 20401094656 Dec 10 23:22 FFHQ.z03 -rw-rw-r-- 1 20401094656 Dec 10…

docker微服务案例

文章目录 建立简单的springboot项目(boot3)boot2建立通过dockerfile发布微服务部署到docker容器编写Dockerfile打包成镜像运行镜像微服务 建立简单的springboot项目(boot3) 1.建立module 2. 改pom <?xml version"1.0" encoding"UTF-8"?> <…

通过反射修改MultipartFile类文件名

1、背景 项目上有这样一个需求&#xff0c;前端传文件过来&#xff0c;后端接收后按照特定格式对文件进行重命名。(修改文件名需求其实也可以在前端处理的) //接口类似于下面这个样子 PosMapping("/uploadFile") public R uploadFile(List<MultipartFile> fil…

鸿蒙HarmonyOS学习手册_入门篇

鸿蒙HarmonyOS学习手册_入门篇 文章目录 鸿蒙HarmonyOS学习手册_入门篇入门快速入门开发准备基本概念UI框架应用模型工具准备 构建第一个ArkTS应用&#xff08;Stage模型&#xff09;-快速入门-入门创建ArkTS工程ArkTS工程目录结构&#xff08;Stage模型&#xff09;构建第一个…

day14 二叉树的遍历 递归遍历 迭代遍历 统一遍历

题目1&#xff1a;递归遍历 题目链接1&#xff1a;144 二叉树的前序遍历 题意 根据二叉树的根节点root&#xff0c;返回它的前序遍历 递归法 前序遍历&#xff1a;中左右 递归三部曲 1) 确定递归函数的参数和返回值 2&#xff09;确定终止条件 3&#xff09;确定单层递…

linux搭建SRS服务器

linux搭建SRS服务器 文章目录 linux搭建SRS服务器SRS说明实验说明搭建步骤推流步骤查看web端服务器拉流步骤final SRS说明 SRS&#xff08;simple Rtmp Server&#xff09;,是一个简单高效的实时视频服务器&#xff0c;支持RTMP/WebRTC/HLS/HTTP-FLV/SRT, 是国人自己开发的一款…

计算机基础面试题 |22.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…