就不解釋什麼是異步調用了,Spring Boot中進行異步調用很簡單java
1.經過使用@Async註解就能簡單的將原來的同步函數變爲異步函數web
package com.winner.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * @author winner_0715 * @date 2018/12/06 */ @Service public class TaskServer { @Async public void doTaskA() throws InterruptedException { System.out.println("TaskA thread name->" + Thread.currentThread().getName()); Long startTime = System.currentTimeMillis(); TimeUnit.SECONDS.sleep(2); Long endTime = System.currentTimeMillis(); System.out.println("TaskA 耗時:" + (endTime - startTime)); } @Async public void doTaskB() throws InterruptedException { System.out.println("TaskB thread name->" + Thread.currentThread().getName()); Long startTime = System.currentTimeMillis(); TimeUnit.SECONDS.sleep(2); Long endTime = System.currentTimeMillis(); System.out.println("TaskB耗時:" + (endTime - startTime)); } }
爲了讓@Async註解可以生效,還須要在Spring Boot的主程序中配置@EnableAsync,以下所示:spring
package com.winner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; /** * @author winner_0715 * @date 2018/12/06 */ @SpringBootApplication @EnableAsync public class SpringBootAsyncApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAsyncApplication.class, args); } }
注: @Async所修飾的函數不要定義爲static類型,這樣異步調用不會生效併發
測試app
package com.winner.web; import com.winner.service.TaskServer; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author winner_0715 * @description: * @date 2018/12/6 */ @RestController public class HelloController { @Resource private TaskServer taskServer; @GetMapping("/async") public String testAsync() throws Exception { System.out.println("主線程 name -->" + Thread.currentThread().getName()); taskServer.doTaskA(); taskServer.doTaskB(); return "Hello World"; } }
任務線程和主線程的名稱不一樣,代表是異步執行的!異步
前面介紹使用@Async註解來實現異步調用了。對於這些異步執行的控制是咱們保障自身應用健康的基本技能。下面介紹經過自定義線程池的方式來控制異步調用的併發。async
第一步,定義一個線程池,好比:函數
package com.winner.threadpool; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author winner_0715 */ @Configuration public class ThreadPoolExecutorConfig { private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1; final ThreadFactory threadFactory = new ThreadFactoryBuilder() // -%d不要少
.setNameFormat("async-task-name-%d") .setDaemon(true) .build(); @Bean("taskExecutor") public Executor taskExecutor() { return new ThreadPoolExecutor(THREADS, 2 * THREADS, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), threadFactory, (r, executor) -> { // 打印日誌,添加監控等
System.out.println("task is rejected!"); }); } }
上面咱們經過使用ThreadPoolExecutor建立了一個線程池測試
在定義了線程池以後,咱們如何讓異步調用的執行任務使用這個線程池中的資源來運行呢?方法很是簡單,咱們只須要在@Async註解中指定線程池名便可,好比:ui
package com.winner.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * @author winner_0715 * @date 2018/12/06 */ @Service public class TaskServer { @Async("taskExecutor") public void doTaskA() throws InterruptedException { System.out.println("MsgServer send A thread name->" + Thread.currentThread().getName()); Long startTime = System.currentTimeMillis(); TimeUnit.SECONDS.sleep(2); Long endTime = System.currentTimeMillis(); System.out.println("MsgServer send A 耗時:" + (endTime - startTime)); } @Async("taskExecutor") public void doTaskB() throws InterruptedException { System.out.println("MsgServer send B thread name->" + Thread.currentThread().getName()); Long startTime = System.currentTimeMillis(); TimeUnit.SECONDS.sleep(2); Long endTime = System.currentTimeMillis(); System.out.println("MsgServer send B耗時:" + (endTime - startTime)); } }
測試
package com.winner.web; import com.winner.service.TaskServer; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author winner_0715 * @description: * @date 2018/12/6 */ @RestController public class HelloController { @Resource private TaskServer taskServer; @GetMapping("/async") public String testAsync() throws Exception { System.out.println("主線程 name -->" + Thread.currentThread().getName()); taskServer.doTaskA(); taskServer.doTaskB(); return "Hello World"; } }
測試結果:
主線程 name -->http-nio-8080-exec-1 MsgServer send A thread name->async-task-name-0 MsgServer send B thread name->async-task-name-1 MsgServer send A 耗時:2001 MsgServer send B耗時:2001