最近在看阿里的JAVA开发手册,说到 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程(new Thread()这种)。
首先在springboot中,它已经给我们提供了很方便的异步和线程池机制。实现异步只要加一个注解@Async,就可以实现了
阿里的JAVA开发手册还说到:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题
Spring
是通过任务执行器(TaskExecutor
)来实现多线程和并发编程,使用ThreadPoolTaskExecutor
来创建一个基于线城池的TaskExecutor
。
1、开启异步任务
本文springboot版本:
2.0.6.RELEASE
在启动类上加注解
@EnableAsync
2、配置类 TaskPoolConfig
@Configuration public class TaskPoolConfig { @Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); //最大线程数30 executor.setPoolSize(30); //线程池名的前缀 executor.setThreadNamePrefix("taskExecutor-"); //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean executor.setWaitForTasksToCompleteOnShutdown(true); //设置线程池中任务的等待时间 executor.setAwaitTerminationSeconds(60); //当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } }
值得留意的是 上面的方法名称为taskExecutor
这里我没有用网上更常见的ThreadPoolTaskExecutor 而是用了ThreadPoolTaskScheduler
注意:
ThreadPoolTaskExecutor 和ThreadPoolTaskScheduler都是ThreadPoolExecutor的包装
区别是ThreadPoolTaskScheduler实现了TaskScheduler接口,仅仅多实现这个接口
3、改造service层服务
编写一个测试接口CommonService ,和其实现类CommonServiceImpl
public interface CommonService { void executeAsync(); }
@Service public class CommonServiceImpl implements CommonService { @Async("taskExecutor") @Override public void executeAsync() { for (int i=0;i<6;i++) { System.out.println( "现在i的值--->>:"+i ); try{ Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } } }
@Async("taskExecutor") 表明executeAsync方法进入的线程池是taskExecutor方法创建的
4、TestController
@RestController public class TestController { @Autowired CommonService commonService; @GetMapping("/test/executeAsync") public String testTASK() { commonService.executeAsync(); return "http请求已结束"; } }
5、测试
我们用浏览器来测试一下:
发现当浏览器显示上述提示时,控制台还在打印以下信息,表明该方法以及异步
6、拓展
虽然现在线程池能其作用,但是还不清楚线程池的具体使用情况,有多少线程在执行,多少在队列中等待呢?所以创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来。这部分参考了这篇博客
springboot线程池的使用和扩展_程序员欣宸的博客-CSDN博客_springboot线程池
public class VisiableThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskScheduler.class); private void showThreadPoolInfo(String prefix){ // ThreadPoolExecutor threadPoolExecutor = getScheduledExecutor(); ScheduledThreadPoolExecutor threadPoolExecutor = getScheduledThreadPoolExecutor(); if(null==threadPoolExecutor){ return; } logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]", this.getThreadNamePrefix(), prefix, threadPoolExecutor.getTaskCount(), threadPoolExecutor.getCompletedTaskCount(), threadPoolExecutor.getActiveCount(), threadPoolExecutor.getQueue().size()); } @Override public void execute(Runnable task) { showThreadPoolInfo("1. do execute"); super.execute(task); } @Override public void execute(Runnable task, long startTimeout) { showThreadPoolInfo("2. do execute"); super.execute(task, startTimeout); } @Override public Future<?> submit(Runnable task) { showThreadPoolInfo("1. do submit"); return super.submit(task); } @Override public <T> Future<T> submit(Callable<T> task) { showThreadPoolInfo("2. do submit"); return super.submit(task); } @Override public ListenableFuture<?> submitListenable(Runnable task) { showThreadPoolInfo("1. do submitListenable"); return super.submitListenable(task); } @Override public <T> ListenableFuture<T> submitListenable(Callable<T> task) { showThreadPoolInfo("2. do submitListenable"); return super.submitListenable(task); } }
注意这里是:
getScheduledThreadPoolExecutor()
然后修改上面的 TaskPoolConfig 文件
// ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); ThreadPoolTaskScheduler executor = new VisiableThreadPoolTaskScheduler();
我们不停的刷新浏览器进行测试,
2019-04-27 13:20:51.693 INFO 18228 --- [nio-8080-exec-7] c.z.o.c.VisiableThreadPoolTaskScheduler : taskExecutor-, 2. do submit,taskCount [88], completedTaskCount [60], activeCount [28], queueSize [0] 现在i的值--->>:0
这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了88个任务,完成了60个,当前有28个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然:P