最近做code review,发现不少地方有自己创建线程使用,基于《阿里巴巴java开发手册》的要求,简单的记录下spring boot项目中如何使用线程池
关于线程的使用方式,大致有三种方式,第一种是继承Threat类,然后重写其中的run方法即可。第二种是实现Runnable接口,也是重写其中的run方法。第三种方式是实现Callable方法,然后调用FutureTask方法和start()方法配合使用。
但在我们熟知的《阿里巴巴java开发手册》中明确规定了不允许用户自己创建线程,必须通过线程池进行创建,至于原因大家也都了解,一是可以规范线程的创建,可以通过相关配置控制开发线程的数量。二是可以把线程统一交给线程池来管理,众所周知,java中有很多池技术的应用,包括像对象池,方法池和连接池等等,都是为了优化资源开销而出现的。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。在我们的Spring boot项目中, 可以用Spring提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskExecutor,直接使用注解启用。
- 首先我们在yml文件中加上线程池的相关配置,具体如下:
#线程池配置
async:
executor:
thread:
#配置核心线程数
core_pool_size: 5
# 配置最大线程数
max_pool_size: 20
# 配置队列大小
queue_capacity: 20
# 配置线程池中的线程的名称前缀
name:
prefix: async-service-
接着新增配置类ThreadPoolConfiguration
@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolConfiguration {
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
log.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
//new ThreadPoolExecutor.AbortPolicy() 线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常
//new ThreadPoolExecutor.DiscardPolicy() 丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
//new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃队列最前面的任务,然后重新提交被拒绝的任务。
//new ThreadPoolExecutor.CallerRunsPolicy() 由调用线程处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
创建一个Service接口AsyncService和其实现类
public interface AsyncService {
/**
* 发送通知邮件
* @param mail 邮箱地址
* @param content 邮件内容
*/
void sendMail(String mail,String content);
}
@Slf4j
@Service
@RequiredArgsConstructor
public class AsyncServiceImpl implements AsyncService {
final private IMailService mailService;
@Override
public void sendMail(String mail,String content) {
mailService.sendSimpleEmail(mail, "邮件题目", content);
}
}
将Service层的服务异步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor"),asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法进入的线程池是asyncServiceExecutor方法创建的。接下来就是在Controller里或者是使用的地方通过注解@Autowired注入这个Service,然后直接使用即可。最后可以通过postMan批量请求测试下接口。