在使用多线程的时候,往往需要继承Thread类
,或者实现Runnable接口
,如果要使用到线程池,还需要来创建Executors
,在Spring中已经做了很好的支持。只要要@EnableAsync
就可以使用多线程。使用@Async
就可以定义一个线程任务。通过Spring提供的ThreadPoolTaskExecutor
就可以使用线程池。
默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的TaskExecutor bean
,要么搜索名为“taskExecutor
”的Executor bean
。如果两者都无法解析,则将使用SimpleAsyncTaskExecutor
来处理异步方法调用。
@Configuration:用于定义配置类,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext
或AnnotationConfigWebApplicationContext
类进行扫描,并用于构建bean定义,初始化Spring容器;
@EnableAsync:开始对异步任务的支持。
package cn.com.codingce.demo.config;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.ThreadPoolExecutor;/*** @author ma*/
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {/*** 核心线程数(默认线程数)*/private static final int corePoolSize = 10;/*** 最大线程数*/private static final int maxPoolSize = 100;/*** 允许线程空闲时间(单位:默认为秒)*/private static final int keepAliveTime = 10;/*** 缓冲队列数*/private static final int queueCapacity = 200;/*** 线程池名前缀*/private static final String threadNamePrefix = "Async-Service-";@Bean("taskExecutor") // bean的名称, 默认为首字母小写的方法名public ThreadPoolTaskExecutor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSize);executor.setMaxPoolSize(maxPoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveTime);executor.setThreadNamePrefix(threadNamePrefix);// 线程池对拒绝任务的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 初始化executor.initialize();return executor;}}
package cn.com.codingce.demo.conrtoller;import cn.com.codingce.demo.service.IndexServiceOne;
import cn.com.codingce.demo.service.IndexServiceTwo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author mxz*/
@RestController
@RequestMapping("index")
public class IndexController {@Autowiredprivate IndexServiceOne indexServiceOne;@Autowiredprivate IndexServiceTwo indexServiceTwo;/*** default*/@GetMapping(value = "/default", produces = "text/html;charset=utf-8")@ApiOperation(value = "default")public String getDefault() throws InterruptedException {indexServiceOne.service1();indexServiceTwo.service2();return "服务运行正常...";}}
package cn.com.codingce.demo.service;public interface IndexServiceOne {void service1();}
package cn.com.codingce.demo.service.impl;import cn.com.codingce.demo.service.IndexServiceOne;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;/*** @author ma*/
@Service("indexServiceOne")
@Slf4j
public class IndexServiceOneImpl implements IndexServiceOne {/*** 发送提醒短信 1*/@Override@Async("taskExecutor")public void service1() {try {log.info("--------start-service1------------");// 模拟耗时Thread.sleep(5000);log.info("--------end-service1------------");} catch (Exception e) {log.error("--------error------------");}}}
package cn.com.codingce.demo.service;public interface IndexServiceTwo {void service2();}
package cn.com.codingce.demo.service.impl;import cn.com.codingce.demo.service.IndexServiceTwo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;/*** @author ma*/
@Service("indexServiceTwo")
@Slf4j
public class IndexServiceTwoImpl implements IndexServiceTwo {/*** 发送提醒短信 2*/@Override@Async("taskExecutor")public void service2() {try {log.info("--------start-service2------------");// 模拟耗时Thread.sleep(1000);log.info("--------end-service2------------");} catch (Exception e) {log.error("--------error------------");}}}
@Async注解来声明一个或多个异步任务,可以加在方法或者类上,加在类上表示这整个类都是使用这个自定义线程池进行操作
接着我们可以创建controller类@Autowired这个service并且调用这其中两个方法,进行连续调用,会发现运行结果是
2023-03-13 12:39:24.653 INFO 21008 --- [Async-Service-1] c.c.c.d.s.impl.IndexServiceOneImpl : --------start-service1------------
2023-03-13 12:39:24.653 INFO 21008 --- [Async-Service-2] c.c.c.d.s.impl.IndexServiceTwoImpl : --------start-service2------------
2023-03-13 12:39:25.660 INFO 21008 --- [Async-Service-2] c.c.c.d.s.impl.IndexServiceTwoImpl : --------end-service2------------
2023-03-13 12:39:29.655 INFO 21008 --- [Async-Service-1] c.c.c.d.s.impl.IndexServiceOneImpl : --------end-service1------------
1、异步方法使用static修饰;
2、异步类没有使用@Component注解(或其他注解)导致Spring无法扫描到异步类;
3、异步方法不能与异步方法在同一个类中;
4、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象;
5、如果使用Spring Boot框架必须在启动类中增加@EnableAsync注解;
6、在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效;
7、调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用!;
8、使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的。
关于线程池的配置还有一种方式,就是直接实现AsyncConfigurer接口,重写getAsyncExecutor方法即可,代码如下:
@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(7);executor.setMaxPoolSize(42);executor.setQueueCapacity(11);executor.setThreadNamePrefix("MyExecutor-");executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new MyAsyncUncaughtExceptionHandler();}
}