@Async異步調用
就不解釋什么是異步調用了,Spring Boot中進行異步調用很簡單
1.通過使用@Async注解就能簡單的將原來的同步函數變為異步函數
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,如下所示:
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類型,這樣異步調用不會生效
測試
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注解來實現異步調用了。對於這些異步執行的控制是我們保障自身應用健康的基本技能。下面介紹通過自定義線程池的方式來控制異步調用的並發。
定義線程池
第一步,定義一個線程池,比如:
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注解中指定線程池名即可,比如:
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