1 介绍

    自定义线程池,有两种实现方式:

  • 更改spring默认的线程池配置,全局生效
  • 自定义新的线程池,指定作用范围
    上一篇文章[《Spring Boot笔记-多线程系列(一)-使用多线程》](https://yxdz.top/2018/12/07/Spring Boot笔记-多线程系列(一)-使用多线程/)使用的就是spring默认的线程池。

2 实现

2.1 更改spring默认线程池配置

2.1.1 介绍

        创建一个类,需要实现AsyncConfigurer即可。

2.1.2 样例

2.1.2.1 目录结构
├── Study
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── top
│   │   │   │       └── yxdz
│   │   │   │           └── study
│   │   │   │               └── spring
│   │   │   │                   └── springboot
│   │   │   │                       ├── thread
│   │   │   │                       │   └── service
│   │   │   │                       │       ├── ITestService.java
│   │   │   │                       │       └── impl
│   │   │   │                       │           └── TestSerivceImpl.java
│   │   │   │                       └── utils
│   │   │   │                           ├── SysThreadValueConfig.java
│   │   │   │                           └── config
│   │   │   │                               └── Thread
│   │   │   │                                   └── SysThreadConfig.java
│   │   │   └── resources
│   │   │       ├── application.yml
│   │   └── test
│   │       └── java
│   │           └── top
│   │               └── yxdz
│   │                   └── study
│   │                       └── StudyApplicationTests.java
2.1.2.2 代码实现
  • application.yml ——自定义了线程池的一些参数信息
spring:
  thread:
    threadNamePrefix: system-thread #线程名称前缀
    corePoolSize: 20 #核心线程池大小
    maxPoolSize: 40 #最大线程数
    keepAliveSeconds: 300 #活跃时间(单位:秒)
    queueCapacity: 50 #队列容量
  • SysThreadValueConfig.java——获取application.yml的自定义线程参数信息
package top.yxdz.study.spring.springboot.utils;

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

@Component
@ConfigurationProperties(prefix = "spring.thread")
public class SysThreadValueConfig {

    private String threadNamePrefix; //线程名称前缀

    private int corePoolSize; //核心线程数

    private int maxPoolSize; //最大线程数

    private int keepAliveSeconds; //线程存活时间

    private int queueCapacity; //队列容量

    public String getThreadNamePrefix() {
        return threadNamePrefix;
    }

    public void setThreadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
    }

    public int getCorePoolSize() {
        return corePoolSize;
    }

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

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

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

    public int getKeepAliveSeconds() {
        return keepAliveSeconds;
    }

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

    public int getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }
}
  • SysThreadConfig.java——更改spring默认的线程池配置类
            将application.yml文件中的自定义参数信息读取,配置到响应位置。
package top.yxdz.study.spring.springboot.utils.config.Thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import top.yxdz.study.spring.springboot.utils.SysThreadValueConfig;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * spring默认的线程池装配类
 */
@Configuration
public class SysThreadConfig implements AsyncConfigurer {

    private static Logger LOG = LoggerFactory.getLogger(SysThreadConfig.class);

    @Autowired
    SysThreadValueConfig sysValueConfig;

    @Override
    public Executor getAsyncExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小
        executor.setCorePoolSize(sysValueConfig.getCorePoolSize());
        //最大线程数
        executor.setMaxPoolSize(sysValueConfig.getMaxPoolSize());
        //队列容量
        executor.setQueueCapacity(sysValueConfig.getQueueCapacity());
        //活跃时间
        executor.setKeepAliveSeconds(sysValueConfig.getKeepAliveSeconds());
        //线程名字前缀
        executor.setThreadNamePrefix(sysValueConfig.getThreadNamePrefix());
        //线程池满的时候,处理新任务的策略
        //CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        //线程实例化
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
                LOG.error("异步任务失败:失败方法【{}】,失败日志【{}】" , method.getName(), throwable.getMessage());
            }
        };
    }
}
package top.yxdz.study;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import top.yxdz.study.spring.springboot.thread.service.ITestService;

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAsync
public class StudyApplicationTests {

    @Autowired
    ITestService iTestService;

    @Test
    public void contextLoads() {

        for(int i=0; i<5; i++){
            iTestService.method1("Async" + i);
        }

        try {
            //等待10s,防止异步代码被强制关闭导致线程抛出异常
            Thread.sleep(10000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
package top.yxdz.study.spring.springboot.thread.service;

public interface ITestService {

    /**
     * 异步测试
     * @param msg
     */
    void method1(String msg);
}
package top.yxdz.study.spring.springboot.thread.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import top.yxdz.study.spring.springboot.thread.service.ITestService;

import java.time.LocalDateTime;


@Service("TestSerivceImpl")
public class TestSerivceImpl implements ITestService {

    private static Logger LOG = LoggerFactory.getLogger(TestSerivceImpl.class);

    @Override
    @Async
    public void method1(String msg){
        try {
            LOG.info(LocalDateTime.now().toString() + msg);
            Thread.sleep(1000);
            LOG.info(LocalDateTime.now().toString() + msg);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
2.1.2.3 执行结果
  • 执行结果
2018-12-14 15:41:53.985  INFO 9998 --- [ system-thread1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:53.985Async0
2018-12-14 15:41:53.985  INFO 9998 --- [ system-thread3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:53.985Async2
2018-12-14 15:41:53.985  INFO 9998 --- [ system-thread4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:53.985Async3
2018-12-14 15:41:53.985  INFO 9998 --- [ system-thread2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:53.985Async1
2018-12-14 15:41:53.985  INFO 9998 --- [ system-thread5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:53.985Async4
2018-12-14 15:41:54.990  INFO 9998 --- [ system-thread3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:54.990Async2
2018-12-14 15:41:54.990  INFO 9998 --- [ system-thread2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:54.990Async1
2018-12-14 15:41:54.990  INFO 9998 --- [ system-thread1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:54.990Async0
2018-12-14 15:41:54.990  INFO 9998 --- [ system-thread4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:54.990Async3
2018-12-14 15:41:54.990  INFO 9998 --- [ system-thread5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:41:54.990Async4
  • 结果说明
            根据日志结果,线程名前缀都是system-thread,也就是我们自己自定义的名称前缀,说明修改默认配置参数信息成功。

2.2 自定义新的线程池

2.2.1 介绍

        自定义线程池与spring默认的线程池的差别在于:

  • 配置类,不需要实现AsyncConfigurer
  • 在使用注解@Async的时候,需要在后面添加("自定义线程池配置的bean名称")来指定使用哪个线程池

2.2.2 样例

2.2.2.1 目录结构
├── Study
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── top
│   │   │   │       └── yxdz
│   │   │   │           └── study
│   │   │   │               └── spring
│   │   │   │                   └── springboot
│   │   │   │                       ├── thread
│   │   │   │                       │   └── service
│   │   │   │                       │       ├── ITestService.java #修改的文件
│   │   │   │                       │       └── impl
│   │   │   │                       │           └── TestSerivceImpl.java #修改的文件
│   │   │   │                       └── utils
│   │   │   │                           ├── SysThreadValueConfig.java
│   │   │   │                           └── config
│   │   │   │                               └── Thread
│   │   │   │                                   └── SysThreadConfig.java
│   │   │   │                                   └── SelfThreadConfig.java #增加的文件
│   │   │   └── resources
│   │   │       ├── application.yml
│   │   └── test
│   │       └── java
│   │           └── top
│   │               └── yxdz
│   │                   └── study
│   │                       └── StudyApplicationTests.java #修改的文件
2.2.2.2 代码实现
  • application.yml——未修改
  • SysThreadValueConfig.java——未修改
  • SelfThreadConfig.java——自定义线程池
            与更改spring默认线程池配置相比,不用实现AsyncConfigurer接口。
package top.yxdz.study.spring.springboot.utils.config.Thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import top.yxdz.study.spring.springboot.utils.SysThreadValueConfig;

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

/**
 * 自定义线程池
 */
@Configuration
public class SelfThreadConfig {
    private static Logger LOG = LoggerFactory.getLogger(SelfThreadConfig.class);

    @Autowired
    SysThreadValueConfig sysValueConfig;

    @Bean
    public Executor myThreadPool(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小
        executor.setCorePoolSize(sysValueConfig.getCorePoolSize());
        //最大线程数
        executor.setMaxPoolSize(sysValueConfig.getMaxPoolSize());
        //队列容量
        executor.setQueueCapacity(sysValueConfig.getQueueCapacity());
        //活跃时间
        executor.setKeepAliveSeconds(sysValueConfig.getKeepAliveSeconds());
        //线程名字前缀
        executor.setThreadNamePrefix("my-thread-pool");
        //线程池满的时候,处理新任务的策略
        //CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        //线程实例化
        executor.initialize();

        return executor;
    }
}
  • StudyApplicationTests.java——测试启动入口
package top.yxdz.study;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import top.yxdz.study.spring.springboot.thread.service.ITestService;

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAsync
public class StudyApplicationTests {

    @Autowired
    ITestService iTestService;

    @Test
    public void contextLoads() {

        for(int i=0; i<5; i++){
            iTestService.method1("system" + i); //使用springn线程池
            iTestService.method2("myself" + i); //使用自定义线程池
        }

        try {
            //等待10s,防止异步代码被强制关闭导致线程抛出异常
            Thread.sleep(10000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
  • ITestService.java
package top.yxdz.study.spring.springboot.thread.service;

public interface ITestService {

    /**
     * 异步测试
     * @param msg
     */
    void method1(String msg);

    /**
     * 异步测试
     * @param msg
     */
    void method2(String msg);
}
  • TestSerivceImpl.java——实现类
            增加了新的函数,使用自定义线程池。使用自定义线程池,需要更改注解为@Async("myThreadPool"),用来表示使用自定义线程池
package top.yxdz.study.spring.springboot.thread.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import top.yxdz.study.spring.springboot.thread.service.ITestService;

import java.time.LocalDateTime;


@Service("TestSerivceImpl")
public class TestSerivceImpl implements ITestService {

    private static Logger LOG = LoggerFactory.getLogger(TestSerivceImpl.class);

    /**
     * 使用系统的线程池
     * @param msg
     */
    @Override
    @Async
    public void method1(String msg){
        try {
            LOG.info(LocalDateTime.now().toString() + msg);
            Thread.sleep(1000);
            LOG.info(LocalDateTime.now().toString() + msg);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    /**
     * 使用个自定义线程池
     * @param msg
     */
    @Override
    @Async("myThreadPool")
    public void method2(String msg){
        try {
            LOG.info(LocalDateTime.now().toString() + msg);
            Thread.sleep(1000);
            LOG.info(LocalDateTime.now().toString() + msg);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
2.2.2.3 执行结果
  • 执行结果
2018-12-14 15:56:38.514  INFO 10292 --- [my-thread-pool1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514myself0
2018-12-14 15:56:38.514  INFO 10292 --- [my-thread-pool2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514myself1
2018-12-14 15:56:38.514  INFO 10292 --- [ system-thread2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514system1
2018-12-14 15:56:38.514  INFO 10292 --- [ system-thread5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514system4
2018-12-14 15:56:38.514  INFO 10292 --- [my-thread-pool5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514myself4
2018-12-14 15:56:38.514  INFO 10292 --- [ system-thread4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514system3
2018-12-14 15:56:38.514  INFO 10292 --- [my-thread-pool3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514myself2
2018-12-14 15:56:38.514  INFO 10292 --- [ system-thread1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514system0
2018-12-14 15:56:38.514  INFO 10292 --- [my-thread-pool4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514myself3
2018-12-14 15:56:38.514  INFO 10292 --- [ system-thread3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:38.514system2
2018-12-14 15:56:39.519  INFO 10292 --- [my-thread-pool1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519myself0
2018-12-14 15:56:39.519  INFO 10292 --- [my-thread-pool4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519myself3
2018-12-14 15:56:39.519  INFO 10292 --- [my-thread-pool5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519myself4
2018-12-14 15:56:39.519  INFO 10292 --- [my-thread-pool2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519myself1
2018-12-14 15:56:39.519  INFO 10292 --- [ system-thread1] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519system0
2018-12-14 15:56:39.519  INFO 10292 --- [my-thread-pool3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519myself2
2018-12-14 15:56:39.519  INFO 10292 --- [ system-thread2] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519system1
2018-12-14 15:56:39.519  INFO 10292 --- [ system-thread4] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519system3
2018-12-14 15:56:39.519  INFO 10292 --- [ system-thread5] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519system4
2018-12-14 15:56:39.519  INFO 10292 --- [ system-thread3] t.y.s.s.s.t.s.impl.TestSerivceImpl       : 2018-12-14T15:56:39.519system2
  • 结果说明
            打印信息是myself为前缀的信息,使用的线程都是my-thread-pool开头,说明使用的是自定义线程池。
            打印信息是system为前缀的信息,使用的线程都是system-thread开头,说明使用的被重新配置的spring线程池。