Spring Boot 到底是單執行緒還是多執行緒

語言: CN / TW / HK

攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第4天,點選檢視活動詳情

前言

我們知道用Spring Boot來構建專案非常的方便,開發過程中我們會使用它來快速的構建專案,自帶tomcat容器、啟動也會將需要配置的bean全部載入到我們的Spring容器裡面。但是在呼叫Controller方法的時候,是單執行緒、還是多執行緒的呢,今天我們來一起研究下。

執行Controller

首先我們通過執行兩個Controller方法,發現列印的日誌,執行這兩個方法分別是不同的執行緒,但是對建立的變數進行了疊加,因為Spring的預設是以單例模式建立的,所以就會導致執行緒不安全。

4ABF2C5E-970D-45EB-8570-0FF05C213A30.png

我們在Çontroller頭部加入@Scope,使它的作用域變成原型模式,這時候我們再次執行方法。 @RequestMapping(“test”) @Scope(“prototype”) @RestController public class TestController {

可以看到依舊是兩個執行緒執行,但是我們變數的操作沒有進行類加,而是分別進行了i++的操作,證明每次的reqeust請求都會建立一個bean。 注意⚠️:我們寫程式碼的時候千萬不能在Çontroller裡面定義常量,然後方法裡面進行操作,會有很大的問題,這裡只是做演示。

313B2929-F70A-42D2-B795-EB0E8FF40F67.png

由此可見Spring Boot中Controller預設是單例模式,並且執行的時候是多執行緒的方式。

@Async

既然提到了這裡,我們就來說下Spring Boot的一個多執行緒首先原理,先來看下我們的非同步執行註解@Async,通過使用註解的方式,在我們需要非同步執行方法的場景下,實現一個非同步執行。 當然也離不開@EnableAsync這個註解,它是一個非同步註解的開關。可以看到使用的是代理的模式。

6F468EC9-F4F5-4D01-B0B8-7FA40CA5DBD6.png

通過看原始碼追蹤到AsyncTaskExecutor 這個介面,看名字就知道是非同步任務的一個執行緒池建立的介面,繼承的TaskExecutor父介面。 35F16DD2-487D-48E8-8616-2506F0C4D7E6.png

可以看到ThreadPoolTaskExecutor這個執行緒池的配置類,可以看到非同步操作其實是由建立執行緒池來完成的,通過向執行緒池提交task任務,用Future類接收,可以獲取非同步任務的執行結果。

72C52829-4D35-4836-8077-E40D81848700.png

F82C6660-8090-497C-981F-59AB0278BC44.png

使用Async

下面來通過自定執行緒池,然後先執行緒池提交我們的非同步任務。

AsyncService非同步方法

``` @Service public class AsyncService {

private Logger logger = LoggerFactory./getLogger/(TestController.class);

@Async(“taskExecutor”)
public String getAsyncMessage(String message) {
    try {
        logger.info(“執行非同步方法,message is “ + message);
        Thread./sleep/(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }


    return message;
}

```

AsyncConfig非同步執行緒池配置

``` public class AsyncConfig {

@Bean(“taskExecutor”)
public Executor taskExecutor(){

    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心執行緒數:執行緒池建立時候初始化的執行緒數
    executor.setCorePoolSize(10);
    // 最大執行緒數:執行緒池最大的執行緒數,只有在緩衝佇列滿了之後才會申請超過核心執行緒數的執行緒
    executor.setMaxPoolSize(20);
    // 緩衝佇列:用來緩衝執行任務的佇列
    executor.setQueueCapacity(500);
    // 允許執行緒的空閒時間60秒:當超過了核心執行緒之外的執行緒在空閒時間到達之後會被銷燬
    executor.setKeepAliveSeconds(60);
    // 執行緒池名的字首:設定好了之後可以方便我們定位處理任務所在的執行緒池
    executor.setThreadNamePrefix(“Async-task-“);
    // 緩衝佇列滿了之後的拒絕策略:由呼叫執行緒處理(一般是主執行緒)
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;

}

```

執行結果,我們可以看見非同步方法通過我們定義的執行緒池提交,沒有按照for迴圈的順序提交,而是實現了非同步的提交。

E6DE8970-61B8-41C0-979A-E089423A0793.png

總結

Spring Boot框架是一個可以實現多執行緒的框架,不是一個執行緒安全的框架,我們要使它變成執行緒安全,還需要整合其他元件,包括用到一些執行緒安全的類,但是可以說明Spring Boot在執行Controller方法的時候確實是以多執行緒的方式執行的,包括非同步的方法。