請參考 Spring Boot 對多線程支持-提高程序執行效率 \
Spring Boot 2.x多線程--使用@Async開啟多線程使用示例
1.在Springboot項目中開啟多線程支持
import java.util.concurrent.Executor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class ThreadConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);//核心線程數目
executor.setMaxPoolSize(Integer.MAX_VALUE);//最大線程數目
executor.setQueueCapacity(20);//隊列中最大的數目
executor.initialize();
return executor;
}
}
2.在application.properties 配置Hikari 可以省略,不是必須
# Hikari will use the above plus the following to setup connection pooling
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=20
spring.datasource.hikari.maximum-pool-size=600
#30min 此屬性控制允許連接在池中閑置的最長時間,此設置僅適用於minimumIdle設置為小於maximumPoolSize的情況 默認:600000(10minutes)
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.pool-name=DatebookHikariCP
#20min 一個連接的生命時長(毫秒),超時而且沒被使用則被釋放(retired),缺省:30分鍾,建議設置比數據庫超時時長少30秒
spring.datasource.hikari.max-lifetime=1200000
#10min 等待連接池分配連接的最大時長(毫秒),超過這個時長還沒可用的連接則發生SQLException, 缺省:30秒
spring.datasource.hikari.connection-timeout=600000
spring.datasource.hikari.connection-test-query=SELECT 1 from dual
3.在需要開啟多線程的方法或類上使用@Async注解 為方法或類開啟線程
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncTaskService {
@Async
public void executeAsyncTask(int i) {
System.out.println("線程" + Thread.currentThread().getName() + " 執行異步任務:" + i);
}
}
4.測試多線程 異步方法和調用方法一定要寫在不同的類中
,如果寫在一個類中,是沒有效果的!
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AsyncTaskServiceTest {
@Autowired
private AsyncTaskService asyncTaskService;
@Test
void testExecuteAsyncTask() {
for (int i = 0; i < 8; i++) {
asyncTaskService.executeAsyncTask(i);
}
}
}
5.運行測試方法,測試多線程。右鍵testExecuteAsyncTask()方法,Run As -> JUnit Test 啟動項目,控制台輸出如下:
6.線程池的排隊和拒絕策略也很重要
請參考線程池排隊策略和拒絕策略
解決java.util.concurrent.RejectedExecutionException
如果在一個方法里a 調用了 異步方法b 和 同步方法c 和 同步方法d。
那么它的執行順序就是 b方法新開一個線程去執行。新線程執行b的同時,主線程會去順序執行c和d。
對多線程,異步的一丟丟自己的理解。不保證對……
- 異步方法:異步方法 就是這個方法里的 操作會開多個線程同時執行。異步方法就是在方法還沒執行完 它就可以返回。是嗎?是的。如果有一個循環,且循環里的操作是互相不依賴的。就可以把 循環里的操作抽成一個方法,為這個方法 開啟新線程。
- 同步方法:這個方法 及其所調用的方法 都在一個線程里執行,並且按先后順序執行,這就是同步方法。如果一件事情 包括3個步驟,並且這3個步驟 都完成,才能把這件事的標志位置為完成狀態。那么這件事就要寫成一個同步方法。也就是說 這個方法 及其所調用的方法 都在一個線程里完成。
假設業務場景:
我們維護了一個賬戶集合,這個賬戶集合里有1000個賬戶。需要為每一個賬戶 去做報表,其中報表包括 主賬戶報表 和 該賬戶的子賬戶報表。
如果同步完成這個業務需求,1.我們可能會寫一個方法generateReportForOne(),這個方法實現的功能是為每一個賬戶去生成它的主賬戶報表和子賬戶報表。2.然后再寫一個方法 generateReport() ,去循環賬戶集合,然后調用 generateReportForOne()方法為所有的賬戶去生成報表。3. 也就是說它的執行流程是: 為賬戶1做完報表,再為賬戶2去做報表,再為賬戶3去做報表……為賬戶1000去做報表。這就是同步方法,所有的事情 都在一個線程里去完成。效率太低。我們可能
如果采用異步方法呢?因為各個賬戶之間生成報表是獨立的,並不互相依賴。也就是說:為賬戶1做報表 和 為賬戶2做報表 互相是獨立的,並不依賴。所以可以采用異步方法來執行這個業務操作。
1.把為一個賬戶生成報表 的方法 generateReportForOne() 開啟一個新線程。2.然后在 generateReport()方法中for循環所有的賬戶,去調用生成報表的方法 generateReportForOne()。3.這樣呢 各個賬戶 生成報表的操作是在獨立的線程里面完成的。 可以同時為很多很多個賬戶生成報表,會極大地提高效率。。。。從15分鍾縮短到1分鍾。