1. 创建自定义线程池
- 我们需要异步调用的时候,很容易就想到多线程的方式,先创建线程池,然后实现 Runnable 或者 Callable
- 接口来创建对象,然后将对象放在线程池中去执行。除了这个,spring 提供了更简单粗暴的方式,这就是本章的主角: @Async 。
- 如果直接使用 @Async,那么默认就是使用 SimpleAsyncTaskExecutor 线程池,由于 SimpleAsyncTaskExecutor 不限制并发线程而且不重用线程,那么直接使用是有风险的,所以需要通过自定义线程池来使用@Async 达到异步调用的目的。
@Configuration @EnableAsync //@EnableAsync 注解主要是为了扫描范围包下的所有 @Async 注解 public class ThreadPoolConfig { @Bean("taskExecutor") public ThreadPoolTaskExecutor taskThreadPool() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(16); //核心线程数目 executor.setMaxPoolSize(900); //指定最大线程数 executor.setQueueCapacity(5); //队列中最大的数目 executor.setThreadNamePrefix("defaultThreadPool_"); //线程名称前缀 //rejection-policy:当pool已经达到max size的时候,如何处理新任务 //CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //对拒绝task的处理策略 executor.setKeepAliveSeconds(60); //线程空闲后的最大存活时间 executor.initialize(); //加载 return executor; } }2.创建异步任务类(service 层)
@Service public class ThreadPoolServiceImpl { // task 1 @Async("taskExecutor") public void Asynctask1() { System.out.println("task1 开始执行"); long startTime = System.currentTimeMillis(); try { Thread.sleep(500L); Asynctask2(); System.out.println("运行成功,task1的线程是"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("出错了!task1的线程是"+Thread.currentThread().getName()); } System.out.println("test 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); } // task 2 @Async("taskExecutor") public void Asynctask2() { System.out.println("task2 开始执行"); long startTime = System.currentTimeMillis(); try { Thread.sleep(500L); System.out.println("运行成功,task2的线程是"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("出错了!task2的线程是"+Thread.currentThread().getName()); } System.out.println("test 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); } }3.接口调用类(controller 层)
@RestController public class ThreadPoolController { @Autowired private ThreadPoolServiceImpl threadPoolService; @Async("taskExecutor") @ResponseBody @RequestMapping(value = "/test1", method = RequestMethod.GET) public String threadPooltest1() { System.out.println("test 开始执行"); threadPoolService.Asynctask1(); return "运行成功threadPooltest1"; } @Async("taskExecutor") @ResponseBody @RequestMapping(value = "/test2", method = RequestMethod.GET) public String threadPooltest2() { System.out.println("test 开始执行"); threadPoolService.Asynctask1(); threadPoolService.Asynctask2(); return "运行成功threadPooltest2"; } }3.1 运行结果: 4.有返回值的异步任务
以上的 task1 和 task2 任务是没有返回值,如果碰到需要获取任务结果就要用到 Future 了。
4.1 service 层:@Service public class ThreadPoolServiceImpl { // task 3 @Async("taskExecutor") public Future4.2 controller 层:Asynctask3() throws Exception{ System.out.println("task3线程名称:" + Thread.currentThread().getName()); long startTime = System.currentTimeMillis(); // int i=8/0; Thread.sleep(1000); System.out.println("test3 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); return new AsyncResult<>("成功"); } }
@RestController public class ThreadPoolController { @Autowired private ThreadPoolServiceImpl threadPoolService; @Async("taskExecutor") @ResponseBody @RequestMapping(value = "/test", method = RequestMethod.GET) public String threadPooltest3() throws Exception { System.out.println("test 开始执行"); System.out.println("threadPooltest3此时正在运行的线程名称:" + Thread.currentThread().getName()); try { Future4.3 调试接口:stringFuture = threadPoolService.Asynctask3(); System.out.println("threadPooltest3此时正在运行的线程名称:" + Thread.currentThread().getName()); System.out.println(stringFuture.get()); return "成功了"+stringFuture.get(); } catch (Exception e) { e.printStackTrace(); System.out.println("出错了"); return "出错了"; } } }
可以明显看出,task1 和 task2 不是异步执行的。原因就是调用 task1 和 task2 方法的是 AsyncTask 对象本身,而不是 spring 启动的时候为其创建的代理对象,没有经过 spring 容器。如果要解决这个问题,就按照这个思路,创建一个代理对象即可。
5.真正的异步调用在 service 中先通过自动装配获取 applicationContext 对象,然后通过 applicationContext 获取 AsyncTask 的代理对象,通过代理对象来调用 task1 和 task2 :
@Component public class ThreadPoolServiceImpl { @Autowired private ApplicationContext applicationContext; public void test(){ System.out.println("test 开始执行"); long startTime = System.currentTimeMillis(); applicationContext.getBean(ThreadPoolServiceImpl.class).task1(); applicationContext.getBean(ThreadPoolServiceImpl.class).task2(); System.out.println("test 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); } @Async("taskExecutor") public Futuretask1() { System.out.println("task1 开始执行"); long startTime = System.currentTimeMillis(); try { Thread.sleep(1000L); } catch (InterruptedException e) { } System.out.println("task1 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); return new AsyncResult<>("task1 success"); } @Async("taskExecutor") public Future task2() { System.out.println("task2 开始执行"); long startTime = System.currentTimeMillis(); try { Thread.sleep(1000L); } catch (InterruptedException e) { } System.out.println("task2 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); return new AsyncResult<>("task2 success"); } }
调用方法:
@GetMapping("/async") public void test() throws ExecutionException, InterruptedException { System.out.println("test 开始执行"); asyncTask.test(); long startTime = System.currentTimeMillis(); System.out.println("test 执行结束,耗时:" + (System.currentTimeMillis() - startTime)); }点击领取源代码:
提取码:2b9w
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)