1、相关注解
@Async是spring为了方便开发人员进行异步调用的出现的,在方法上加入这个注解,spring会从线程池中获取一个新的线程来执行方法,实现异步调用
@EnableAsync表示开启对异步任务的支持,可以放在springboot的启动类上,也可以放在自定义线程池的配置类上,具体看下文
2、自定义线程池
import lombok.Data;
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;
@EnableAsync
@Configuration
public class ThreadPoolConfig {
ThreadPoolProperties properties = new ThreadPoolProperties();
@Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数目 : 池中会保留的最多线程数
executor.setCorePoolSize(properties.getCorePoolSize());
//最大线程数目 = 核心线程+救急线程的最大数目
executor.setMaxPoolSize(properties.getMaxPoolSize());
executor.setQueueCapacity(properties.getQueueCapacity());
executor.setThreadNamePrefix(properties.getThreadNamePrefix());
// 设置线程保持活跃的时间(默认:60)
//生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
executor.setKeepAliveSeconds(properties.getKeepAliveTime());
// 当任务完成后,长时间无待处理任务时,销毁线程池
executor.setWaitForTasksToCompleteOnShutdown(properties.isWaitForTasksToCompleteOnShutdown());
executor.setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds());
// 设置任务拒绝策略
/**
* 4种
* ThreadPoolExecutor类有几个内部实现类来处理这类情况:
- AbortPolicy 丢弃任务,抛RejectedExecutionException
- CallerRunsPolicy 由该线程调用线程运行。直接调用Runnable的run方法运行。
- DiscardPolicy 抛弃策略,直接丢弃这个新提交的任务
- DiscardOldestPolicy 抛弃旧任务策略,从队列中踢出最先进入队列(最后一个执行)的任务
* 实现RejectedExecutionHandler接口,可自定义处理器
*/
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
@Data
class ThreadPoolProperties {
/**
* 核心线程数(默认是1):若是IO密集型,cpu核心数*2,若是cpu密集型,cpu核心数
* 核心线程会一直存活,及时没有任务需要执行
* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
* 注意:当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
*/
private int corePoolSize = 2;
/**
* 最大线程数,系统默认Integer.MAX_VALUE
*/
private int maxPoolSize = 5;
/**
* 允许线程空闲时间(单位:默认为秒,默认60S)
* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
* 如果allowCoreThreadTimeout=true,则会直到线程数量=0
*/
private int keepAliveTime;
/**
* 缓冲队列大小,系统默认Integer.MAX_VALUE
* 注意:这个值肯定要改小,不然任务陡增时,都堆在队列中(队列值大),
* 核心线程数就那几个,无法很快执行队列中的任务,
* 就会延长响应时间,阻塞任务
*/
private int queueCapacity = 8;
/**
* 线程池名前缀,用于监控识别
*/
private String threadNamePrefix = "asyncTaskExecutor-";
/**
* 允许核心线程超时(默认false)
*/
private boolean allowCoreThreadTimeout = false;
/**
* 当任务完成后,长时间无待处理任务时,销毁线程池
*/
private boolean waitForTasksToCompleteOnShutdown = false;
private int awaitTerminationSeconds;
}
}
3、使用@Async调用多线程
@Service
@EnableAsync
public class ThreadPoolServiceImp implements ThreadPoolService {
@Async("taskExecutor")
@Override
public void F1(int num) {
try {
System.out.println(
Thread.currentThread().getName()
+"\t"
+Thread.currentThread().getId()
+"\t"
+ new Date()
+"\t"
+"执行线程: "
+num);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
}
4、验证使用
public class ThreadPoolControllerTest extends BasiServiceApplicationTests {
@Autowired
private ThreadPoolService threadPoolService;
@Test
public void f1() throws InterruptedException {
for (int i = 0; i < 50; i++) {
threadPoolService.F1(i);
}
sleep(10*1000);
}
}
5、未防止 线程中断:java.lang.InterruptedException: sleep interrupted
测试代码添加:
sleep(10*1000);
6、@Async失效情况:
1、异步方法使用static修饰
2、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
3、异步方法不能与被调用的异步方法在同一个类中
4、类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
5、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
7、线程池处理任务流程:
当往线程池中提交新任务时,线程池主要流程如下:
核心线程数 -> 线程队列 -> 最大线程数 -> 拒绝策略
如果池中任务数 < corePoolSize (核心线程数),创建新线程立即执行任务
如果池中任务数 > corePoolSize,新任务放到缓存队列当中等待执行
队列满,线程数量<maxPoolSize,新建线程立即执行任务
队列满,线程数量>=maxPoolSize,使用拒绝策略拒绝