栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot自定义线程池(9)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot自定义线程池(9)

前言

        实际项目中必不可少的会使用到异步处理,典型的比如注册后的短信通知或者邮件进行账户激活等。Java可以用4种方式来创建线程,继承Thread类、实现Runnable接口、使用Callable和Future、使用线程池例如用Executor。有时候项目中可能有多个不同的业务类型都需要使用到异步线程,这时候我们可以根据不同的业务创建不同的线程池,让单一线程池专一做一件事情,分工明确,方便管理,本文就是介绍这种自定义线程池的创建方式。

线程池配置文件

        为了配置文件清爽,单独分出一个配置文件,专门配置各种线程池,application-thread.yml 配置如下,本文就以异步发送短信和邮件为例,配置两个线程池,实际配置数据请根据自己项目情况进行调整:

  # mail线程池配置
  mail-thread-pool:
    corePoolSize: 20 #核心线程数
    maxPoolSize: 40  #最大线程数
    keepAliveSeconds: 60 #空闲线程存活时间
    queueCapacity: 1000  #等待队列大小
  # message线程池配置
  message-thread-pool:
    corePoolSize: 20
    maxPoolSize: 40
    keepAliveSeconds: 60
    queueCapacity: 1000

        然后在application-thread.yml 主配置文件中引用上面配置:

spring:
  profiles:
    include: #不能换行, 多个用英文逗号分隔
      ds,csdn,em,thread

线程池常量类

        定义一个ThreadPoolConstants.java父类pojo常量类封装线程池配置信息:

package com.liberal.common.config.thread;

public class ThreadPoolConstants {

    //核心线程数
    private Integer corePoolSize = 8;
    //最大线程数
    private Integer maxPoolSize = 16;
    //等待队列长度
    private Integer queueCapacity = 128;
    //空闲线程存活时间
    private Integer keepAliveSeconds = 60;

    public Integer getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(Integer corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public Integer getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(Integer maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public Integer getKeepAliveSeconds() {
        return keepAliveSeconds;
    }

    public void setKeepAliveSeconds(Integer keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
    }

    public Integer getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(Integer queueCapacity) {
        this.queueCapacity = queueCapacity;
    }
}

        定义邮件pojo常量类MailThreadPoolConstants.java继承ThreadPoolConstants.java用来封装邮件线程池配置:

package com.liberal.common.config.thread;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "mail-thread-pool")
public class MailThreadPoolConstants extends ThreadPoolConstants {
}

        定义短信pojo常量类MessageThreadPoolConstants.java继承ThreadPoolConstants.java用来封装邮件线程池配置:

package com.liberal.common.config.thread;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "message-thread-pool")
public class MessageThreadPoolConstants extends ThreadPoolConstants {
}
线程池配置类

        编写自定义线程池配置类ThreadPoolConfig.java,其中@EnableAsync注释可以在配置类,也可以在启动类,二选一,不可少。可以指定线程名称,区分业务,方便日志排查,指定拒绝策略,详细配置如下:

package com.liberal.common.config.thread;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class ThreadPoolConfig {

    private final MailThreadPoolConstants mailThreadPoolConstants;

    private final MessageThreadPoolConstants messageThreadPoolConstants;

    @Autowired
    public ThreadPoolConfig(MailThreadPoolConstants mailThreadPoolConstants, MessageThreadPoolConstants messageThreadPoolConstants) {
        this.mailThreadPoolConstants = mailThreadPoolConstants;
        this.messageThreadPoolConstants = messageThreadPoolConstants;
    }

    //邮件异步执行类
    @Bean(name = "mailThreadExecutor")
    public Executor getMailThreadExecutor() {
        return initExecutor(mailThreadPoolConstants, "mailThreadExecutor-");
    }

    //短信异步执行类
    @Bean(name = "messageThreadExecutor")
    public Executor getMessageThreadExecutor() {
        return initExecutor(messageThreadPoolConstants, "messageThreadExecutor-");
    }

    private ThreadPoolTaskExecutor initExecutor(ThreadPoolConstants constants, String prefix) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(constants.getCorePoolSize());
        executor.setMaxPoolSize(constants.getMaxPoolSize());
        executor.setKeepAliveSeconds(constants.getKeepAliveSeconds());
        executor.setQueueCapacity(constants.getQueueCapacity());

        // 设置默认线程名称
        executor.setThreadNamePrefix(prefix);
        // 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteonShutdown(true);
        return executor;
    }
}
异步操作示例

        直接使用Spring提供的@Async注解就可以实现异步效果,但这种方式非常消耗cpu资源,实际开发禁止使用,解决途径就是使用该注解配合自定义的线程池, 其中EmailUtil类请看该文提取。

package com.liberal.async;

import com.liberal.common.bean.email.EmailBean;
import com.liberal.common.bean.email.EmailUser;
import com.liberal.common.utils.email.EmailUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;



@Slf4j
@Component
public class AsyncTask {

    //@Async 这种方式非常消耗cpu资源,实际开发禁止使用
    @Async("messageThreadExecutor") //调用自定义线程池,中小型项目推荐使用,大型项目推荐使用MQ
    public void sendMessage() throws InterruptedException {
        log.info("异步发送短信开始...");
        Thread.sleep(3000);
        log.info("异步发送短信结束...");
    }


    @Async("mailThreadExecutor")
    public void sendEmail() {

        EmailBean bean = new EmailBean();
        bean.setSmtpHost("smtp.qq.com");//此处测试qq服务器,其他邮箱类似,如126邮箱:smtp.126.com
        bean.setEmailTitle("注册成功提醒");
        bean.setEmailContent("您好,
    恭喜您注册账号成功!"); // 发件人 bean.setFromUser(new EmailUser("Itelligence", "factorycode@foxmail.com", "xxxxxxxxx"));//昵称、邮箱地址、发件人stmp授权码 // 收件人 ArrayList toList = new ArrayList<>(); toList.add(new EmailUser("factorycode@sina.com")); bean.setRecipientType_TO(toList); // 抄送人 ArrayList ccList = new ArrayList<>(); ccList.add(new EmailUser("weny.yang@sun***.com")); bean.setRecipientType_CC(ccList); // 密送人 ArrayList bccList = new ArrayList<>(); bccList.add(new EmailUser("factorycode@foxmail.com")); bean.setRecipientType_BCC(bccList); // 附件 List annexPaths = new ArrayList<>(); annexPaths.add("/Users/ywy/IdeaProjects/Intelligence/HELP.md"); bean.setAnnexPaths(annexPaths); EmailUtil.sendEmail(bean); } }
调用类
package com.liberal.controller;


import com.liberal.async.AsyncTask;
import com.liberal.common.bean.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;


@Slf4j
@RestController
public class HelloWorldController {

    @Resource
    private AsyncTask syncTask;

    @RequestMapping("/register")
    public Response register() throws Exception {
        //模拟注册
        //Do something
        log.info("注册成功");

        //异步发送短信邮件
        syncTask.sendMessage();
        syncTask.sendEmail();

        return Response.success("注册成功!", null);
    }

}
启动测试

        启动项目,浏览器模拟注册,瞬间返回处理结果,无需等待发送完短信和邮件,用户体验爽歪歪:

        再来看看控制台信息,根据日志打印时间顺序可知,同步方法执行完立马响应了浏览器,异步执行部分分别开启了两个独立的线程执行,线程名称分别是messageThreadExecutor-1、mailThreadExecutor-1,细心的伙伴肯定发现了线程名称前缀正式我在配置类中定义的名称,这样方便我们进行日志阅读和问题排查。

 邮件信息

        查看邮箱,邮件发送成功,非常巴适非常奈斯。

上文只是使用邮件、短信进行案例演示,请根据自己项目实际生产情况编写自定义线程池。

如果有学到,就点个赞吧,一起进步。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/333239.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号