第02組每週小結(3/3)
技術標籤:springboot
一 介紹
工作中經常涉及非同步任務,通常是使用多執行緒技術,比如執行緒池ThreadPoolExecutor,但使用Executors容易產生OOM,需要手動使用ThreadPoolExecutor建立執行緒池;在springboot使用 @async 可以實現非同步呼叫,配置執行緒池引數,可以簡單的實現多執行緒的執行緒池效果,從而簡化開發,避免OOM;
二 非同步呼叫
2.1無返回非同步
我們知道同步執行就是按照程式碼的順序執行,而非同步執行則是無序,在springboot中使用實現非同步呼叫函式非常簡單,首先在啟動類上加上@EnableAsync
註解;
/**
* @Author lsc
* <p> </p>
*/
@SpringBootApplication
@EnableAsync
public class AsyncRunApp {
public static void main(String[] args) {
SpringApplication.run(AsyncRunApp.class, args);
}
}
其次,在函式上標上@sync
註解,表示非同步呼叫
@Async
public void taskOne() throws Exception {
System. out.println("任務一");
}
@Async
public void taskTwo() throws Exception {
System.out.println("任務二");
}
測試程式碼
@Autowired
Task task;
@Test
public void test() throws Exception {
task.taskOne();
task.taskTwo();
}
如果按照同步執行邏輯會先執行任務一,然後再執行任務二,如果是非同步執行,則無序,可能任務一先執行,也可能任務二先執行;
2.2 有返回值回撥
有時候要知道任務是否執行完成,再繼續做其它的業務邏輯,就需要使用到Future介面,其含義是在執行非同步任務後會給一個回撥函式,我們只要設定回撥資訊,就可以知道任務是否正確執行完成;我們對非同步函式,新增 Future
返回值型別,使用 new AsyncResult<>()
設定回撥資訊;
@Component
public class Task {
@Async
public Future<String> taskOne() throws Exception {
System.out.println("任務一");
return new AsyncResult<>("任務一執行完成");
}
@Async
public Future<String> taskTwo() throws Exception {
System.out.println("任務二");
return new AsyncResult<>("任務二執行完成");
}
}
測試程式碼如下, 等待2個任務全部完成後就打印出返回值資訊
@Autowired
Task task;
@Test
public void test() throws Exception {
Future<String> str1 = task.taskOne();
Future<String> str2 = task.taskTwo();
while (true){
// 如果任務都做完就執行如下邏輯
if (str1.isDone() && str2.isDone()){
System.out.println(str1.get()+":"+str2.get());
break;
}
}
}
執行輸出
任務二
任務一
任務一執行完成:任務二執行完成
關注公眾號 知識追尋者 獲取原創PDF,面試題集,最新成熟技術棧;
三 執行緒池
在非同步掉用中使用的@Async
註解,預設的執行緒池大小如下;
# 核心執行緒數
spring.task.execution.pool.core-size=8
# 最大執行緒數
spring.task.execution.pool.max-size=16
# 空閒執行緒存活時間
spring.task.execution.pool.keep-alive=60s
# 是否允許核心執行緒超時
spring.task.execution.pool.allow-core-thread-timeout=true
# 執行緒佇列數量
spring.task.execution.pool.queue-capacity=100
# 執行緒關閉等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 執行緒名稱字首
spring.task.execution.thread-name-prefix=task-
一般情況下,我們都需要手動建立執行緒池,使用 ThreadPoolTaskExecutor 類進行配置;這邊設定了執行緒字首名稱,等下測試時就可以判定是否執行緒池配置成功;
@Configuration
public class PoolConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設定核心執行緒數
executor.setCorePoolSize(10);
// 設定最大執行緒數
executor.setMaxPoolSize(15);
// 設定佇列容量
executor.setQueueCapacity(20);
// 設定執行緒活躍時間(秒)
executor.setKeepAliveSeconds(60);
// 設定預設執行緒名稱
executor.setThreadNamePrefix("zszxz-");
// 設定拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任務結束後再關閉執行緒池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
在task類中加上 新的一個方法如下
@Async
public void sayHello(String name) {
LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
}
使用測試類進行測試
@Test
public void testPool() throws Exception {
task.sayHello("公眾號:知識追尋者");
}
執行結果如下,日誌打印出執行緒名稱為zszxz-1
;
有時候,一個專案中如果配置了多個執行緒池,如下格式
@Bean("pool1")
public TaskExecutor taskExecutor1() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//....
return executor;
}
@Bean("pool2")
public TaskExecutor taskExecutor2() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//....
return executor;
}
@Bean("pool3")
public TaskExecutor taskExecutor3() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//....
return executor;
}
在使用 @Async註解時就需要指明具體使用的執行緒池,如下格式
@Async("pool1")
public void sayHello1(String name) {
LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
}
@Async("pool2")
public void sayHello1(String name) {
LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
}
原始碼地址:https://github.com/zszxz/study-springboot