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.java2.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());
}
};
}
}StudyApplicationTests.java——测试启动的入口
内容同Spring Boot笔记—多线程系列(一)—使用多线程一样,未修改。
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();
}
}
}ITestService.java内容同Spring Boot笔记—多线程系列(一)—使用多线程一样,未修改。
package top.yxdz.study.spring.springboot.thread.service;
public interface ITestService {
/**
* 异步测试
* @param msg
*/
void method1(String msg);
}TestSerivceImpl.java——测试类
内容同Spring Boot笔记—多线程系列(一)—使用多线程一样,未修改。
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线程池。
















