天天看點

SpringBoot 最簡單的使用異步線程案例 @Async

在網上找了很多很多資料,使用多線程、異步線程等等,很多配置,方法也多多;

那麼,我向來都是以簡單,夠用為目标,下面我介紹的就是我認為已經非常非常簡單的異步線程使用的方法了。

說到簡單,當然是使用注解。

進入正題:

先上個目錄結構:

SpringBoot 最簡單的使用異步線程案例 @Async

好了,我們這次是在springboot裡面使用的,不用導啥包。

我們先創個異步線程的配置類, 我的叫ThreadConfig,你們随意:

package com.async.config;

import java.util.concurrent.Executor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;



@Configuration
@ComponentScan("com.async.service")
@EnableAsync
public class ThreadConfig  {
    /**
     * 執行需要依賴線程池,這裡就來配置一個線程池
     * @return
     */

    // 當池子大小小于corePoolSize,就建立線程,并處理請求
    // 當池子大小等于corePoolSize,把請求放入workQueue(QueueCapacity)中,池子裡的空閑線程就去workQueue中取任務并處理
    // 當workQueue放不下任務時,就建立線程入池,并處理請求,如果池子大小撐到了maximumPoolSize,就用RejectedExecutionHandler來做拒絕處理
    // 當池子的線程數大于corePoolSize時,多餘的線程會等待keepAliveTime長時間,如果無請求可處理就自行銷毀

   @Bean("getExecutor")
    public Executor getExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //設定核心線程數
        executor.setCorePoolSize(10);
        //設定最大線程數
        executor.setMaxPoolSize(100);
        //線程池所使用的緩沖隊列
        executor.setQueueCapacity(250);
        //設定線程名
        executor.setThreadNamePrefix("JcTest-Async");
        //設定多餘線程等待的時間,機關:秒
        //executor.setKeepAliveSeconds();
        // 初始化線程
        executor.initialize();
        return executor;
    }
}      

向來我都不做代碼過多的解釋,因為我的代碼注釋 已!經!足!夠!詳!細 ! 了!

不過還是得提一下這倆注解,

@ComponentScan("com.async.service") 這個是告訴全世界,我即将開啟異步線程的業務方法是哪個      
@EnableAsync 這個! 必須有!  告訴全世界允許我使用異步線程      

好了,接下來我們建立一下業務層的東西吧:

AsyncTestService:

package com.async.service;






public interface AsyncTestService {
    /**
     * 這裡将會在impl裡标注為異步任務,在執行此方法的時候,會單獨開啟線程來執行
     */

   void function1() throws InterruptedException;


    void function2();
}      

AsyncTestServiceImpl: 

package com.async.service.impl;



import com.async.service.AsyncTestService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.UUID;


@Service
public class AsyncTestServiceImpl implements AsyncTestService {
    /**
     * 這裡進行标注為異步任務,在執行此方法的時候,會單獨開啟線程來執行
     */
    @Async("getExecutor")
    public void function1() throws InterruptedException {
        System.out.println("f1 : " + Thread.currentThread().getName() + "   " + UUID.randomUUID().toString());
//        try {
//            Thread.sleep(10000);
//            System.out.println("EEEE");
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //故意等10秒,那麼異步線程開起來,這樣明顯看到 2方法不用等1方法執行完再調用了
        Thread.sleep(10000);
        System.out.println("EEEE");
    }

    @Async("getExecutor")
    public void function2() {
        System.out.println("f2 : " + Thread.currentThread().getName() + "   " + UUID.randomUUID().toString());
        try {
            Thread.sleep(100);
            System.out.println("aaaa");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}      

代碼不走讀了,但是必須得提下注解:

@Async 這個是告訴全世界,這裡! 隻要被調用,就是會開啟一個異步線程。

至于後面加上("getExecutor"),是為了指定讀取自己寫的配置資訊例如線程名稱那些。

最後是TestController:

package com.async.test;

import com.async.service.AsyncTestService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {
    @Autowired
    AsyncTestService asyncTestService;

    @GetMapping("/test")
    public void test() throws InterruptedException {
//        for (int i = 0; i < 10; i++) {
//            asyncTestService.function1(); // 執行異步任務
//            asyncTestService.function2();
//        }

        asyncTestService.function1(); // 執行異步任務
        asyncTestService.function2();

    }
}      

啟動類沒啥改變:

package com.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AsyncApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }
}      

好了,跑一下:

SpringBoot 最簡單的使用異步線程案例 @Async

結合代碼看看,懂了吧應該。  

稍微解釋一哈:

asyncTestService.function1(); // 執行異步任務

asyncTestService.function2();

本來按照正常,肯定是按順序執行的,也就是先編譯完方法1,輸出所有東東,再到方法2;

然後我們配了異步,而且在方法1那邊sleep了蠻久的, 是以你看到輸出結果,顯然方法2自己單飛了,并沒有等方法1;

這結果是我們想要的。

最後提醒一下, 有沒有發現,我這個例子分了好多層,而且連impl都分了的。其實不分那麼細也沒問題,但是!!!

有個需要注意的!  利用@Async注解的方法, 不能跟調用它的方法放在同個類裡面!!!! 否則會循環依賴錯誤!!!

還有一個需要注意的,@Async所修飾的函數不要定義為static類型,這樣異步調用不會生效!

繼續閱讀