springboot启动时开启一个或多个线程 您所在的位置:网站首页 怎么开启一个线程服务 springboot启动时开启一个或多个线程

springboot启动时开启一个或多个线程

2023-12-27 08:29| 来源: 网络整理| 查看: 265

需求背景

最近项目基于springboot做微信机器人,从网上看了一下,一般都是单个用户登录,然后一个线程死循环,类似监听器,如果有信息就处理信息。但是,我们的要求是需要可以多个用户登录,所以,每次登录一个用户,开启一个线程,用户意外退出或者接口响应错误的情况下,该线程回收。

具体流程图如下:

设计思路很简单,但是问题真多。具体遇到的问题,在另一篇中详细讲解吧。此处,就想解决线程池争取资源时间太长的问题,想放弃前面的思路,直接在项目启动的时候开启一个监听器线程,循环处理。也就是上面流程图中async的监听器,原来是每个登录请求都会创建,现在设计为程序启动后直接开启一个线程监听,从队列中获取信息,新来的机器人加入到队列末尾,依次遍历,同微信服务器保持心跳。虽然两者都有很多弊端,能支持4-8个机器人同时在线即可。

开启线程方法1

首先想到方法是在Application的main方法中创建一个线程:

1234567891011public static void main(String[] args) { System.setProperty("https.protocols", "TLSv1"); System.setProperty("jsse.enableSNIExtension", "false"); SpringApplication application = new SpringApplication(WxbotApplication.class); application.setBannerMode(Banner.Mode.CONSOLE); application.addListeners(new PropertiesListener("classpath*:*.properties")); application.run(args); // 开启监听线程 Thread listener = new Thread(new RobotListenJob()); listener.start();}

但是如果我的RobotListenJob里面有些service需要注入的时候,这时候,虽然spring给我留了接口可获取容器中的bean,但是不太优雅,甚至是有些部署环境还会无法启动该线程,如果是单独的jar包服务,方法1和方法2均是可行的,但是web中不一定可行。

12345678910111213141516171819202122// RobotListenJob.javapublic class RobotListenJob implements Runnable { private WxbotService wxbotService; public RobotListenJob() { wxbotService = getWxbotService(); } @Override public void run() { if (wxbotService != null) { wxbotService.listen(); } } /** * 从spring上下文容器中获取wxbotService实例 */ private WxbotService getWxbotService() { return SpringContextUtils.getBean(WxbotService.class); }}

这种方式,可能就是wxbotService对象还没有实例化,同样还有一种就是在Application中获取wxbotService实例,传给RobotListenJob的构造函数,代码如方法2。

方法2123456789public static void main(String[] args) { System.setProperty("https.protocols", "TLSv1"); System.setProperty("jsse.enableSNIExtension", "false"); ApplicationContext ctx = SpringApplication.run(WxbotApplication.class, args); // 开启监听线程 Thread listener = new Thread(new RobotListenJob(ctx.getBean(WxbotService.class))); listener.start();}

RobotListenJob增加有参构造函数:

12345678910111213public class RobotListenJob implements Runnable { private WxbotService wxbotService; public RobotListenJob(WxbotService wxbotService) { this.wxbotService = wxbotService; } @Override public void run() { if (wxbotService != null) { wxbotService.listen(); } }}

上述方法1和方法2,如果你的web项目继承了SpringBootServletInitializer,且在protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)该方法中构建了Application.class,是没有问题的,如下。

123456789public class SpringBootStartApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 注意这里要指向原先用main方法执行的WxbotApplication启动类 // 此处的WxbotApplication就是文章中所说的Application,里面有一个main方法 return builder.sources(WxbotApplication.class); }} 方法3

下面我们看看终极大招一,使用@Component的注解方式来开启一个线程,还是利用了spring注解的原理以及spring初始化bean的方式(无参构造器),其他没什么新意。直接通过构造函数,开启一个线程。

1234567891011121314151617181920212223242526272829// RobotListenJob.java@Componentpublic class RobotListenJob implements DisposableBean, Runnable { @Resource private WxbotService wxbotService; private Thread thread; // private boolean someCondition; public RobotListenJob() { thread = new Thread(this); } @Override public void run() { if (wxbotService != null) { // listen方法是一个带条件的死循环 wxbotService.listen(); } // 也可以根据条件在该方法中开启一个 //while(someCondition) { // doStuff(); //} } @Override public void destroy(){ // someCondition = false }} 方法4

springboot启动过程产生的6种事件,包括ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent、``和ApplicationFailedEvent,通过ApplicationReadyEvent事件可以实现系统启动完后做一些系统初始化的操作。

ApplicationStartingEvent: springboot应用启动且未作任何处理(除listener注册和初始化)的时候发送ApplicationStartingEvent ApplicationEnvironmentPreparedEvent: 确定springboot应用使用的Environment且context创建之前发送这个事件 ApplicationPreparedEvent: context已经创建且没有refresh发送个事件 ApplicationStartedEvent: context已经refresh且application and command-line runners(如果有) 调用之前发送这个事件 ApplicationReadyEvent: application and command-line runners (如果有)执行完后发送这个事件,此时应用已经启动完毕 ApplicationFailedEvent: 应用启动失败后产生这个事件

创建RoboJobListener事件监听器,实现onApplicationEvent方法,在ApplicationReadyEvent就绪时,调用WxbotService服务。

1234567891011121314151617181920212223242526public class RoboJobListener implements ApplicationListener{ @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartingEvent){ return; } if (event instanceof ApplicationEnvironmentPreparedEvent){ return; } if (event instanceof ApplicationPreparedEvent){ return; } if (event instanceof ApplicationStartedEvent){ return; } if (event instanceof ApplicationReadyEvent){ ApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext(); WxbotService wxbotService = context.getBean(WxbotService.class); wxbotService.listen(); return; } if (event instanceof ApplicationFailedEvent){ return; } }}

注册监听器listener,在SpringApplication初始化的时候添加进去。

1234567@SpringBootApplicationpublic class WxbotApplication {public static void main(String[]args){ new SpringApplicationBuilder().sources(WxbotApplication.class) .listeners(new RoboJobListener()).run(args); }} 方法5

除了springboot启动过程产生的6种事件中通过ApplicationReadyEvent事件可以实现系统启动完后做一些系统初始化的操作外,springboot应用还可以通过ApplicationRunner(CommandLineRunner也类似)这种方式也可以实现同样的功能。

1234567891011121314151617181920@Componentpublic class RobotListenJob implements ApplicationRunner { @Resource private WxbotService wxbotService; @Override public void run(ApplicationArguments args) { Thread thread = new Thread(() -> { if (wxbotService != null) { // listen方法是一个带条件的死循环 wxbotService.listen(); } // 也可以根据条件在该方法中开启一个 //while(someCondition) { // doStuff(); //} }); thread.start(); }}


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有