Featured image of post 簡單的聊聊SpringBoot中的非同步操作@Async

簡單的聊聊SpringBoot中的非同步操作@Async

沒想到能這麼快接觸到非同步操作

在SpringBoot中要實現非同步有很多種方式,比如說你自己手寫一個ThreadPool、或是藉由外部的RabbitMQ都可以實現非同步的方法。

那這邊我選擇實做的方法是使用SpringBoot提供的@Async,什麼是非同步呢?

舉個例子假設我在做櫃檯,現在有10個客人要辦帳戶,每個客人過來我這邊,都要寫10分鐘的表格,寫完後我在幫他們登記,花兩分鐘。如果一個客人一個客人這樣過來,我需要花120分鐘才可以消化掉。但如果我可以先把表格發給全部10個人填,這樣我只要花10+10*2=30分鐘就可以。

這就是非同步操作,簡而言之就是避免無意義的等待

由於網路上寫的教學實在都太複雜了,我想寫一篇最最最基本的那一種,不需要配什麼config,連什麼資料庫,一切都是最基本的樣子

Github連結

https://github.com/Hoxton019030/SpringBootAsync

如何創建一個非同步方法

非常簡單!只要在方法上加上@Async的註解就可以囉!就像這樣

1
2
3
4
5
6
7
8
9
		@Async
  	public void testAsync() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("非同步測試執行,執行緒為:" + Thread.currentThread().getName());
    }

實際感受何謂非同步執行

我們可以寫一個controller來呼叫我們剛剛寫好的testAsync()

1
2
3
4
5
6
7
    @GetMapping("/")
    public String testAsync() {
        System.out.println("開始測試,執行緒為 - " + Thread.currentThread().getName());
        asyncTask.testAsync(); //這東西會印出非同步測試執行,執行緒為:Async-Service-1
        System.out.println("測試結束,執行緒為 - " + Thread.currentThread().getName());
        return "非同步測試";
    }

專案執行完後,可以輸入

http://localhost:8080/

訪問endPoint,可以看到"非同步測試執行,執行緒為:這句話打印出來的時間點,遠小於測試結束,執行緒為 - 這句話,這就是非同步執行帶來的好處。

iShot_2023-10-08_19.41.45

繼續感受非同步執行

之前在資策會的專案中,有一個功能是要寄出Email的,當時量沒那麼大,所以沒感覺,實際工作之後發現寄Email這件事情要花很多時間在等待上面,這就是一個很適合用非同步去處理的事情。在這邊我一樣使用Thread.sleep()去模擬寄信這件事情

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Async
    public void sendEmail(int mailNumber) {
        for (int i = 1; i < mailNumber + 1; i++) {
            try {
                Thread.sleep(1000);
                System.out.println("寄出第" + i + "封信,所使用的執行緒是" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                System.out.println("寄出第" + i + "封信時發生錯誤!");
                throw new RuntimeException(e);
            }

        }
    }
1
2
3
4
5
6
7
8
    @GetMapping("/email")
    public String sendEmail() {
        System.out.println("開始測試,執行緒為 - " + Thread.currentThread().getName());
        asyncTask.sendEmail(10);
        System.out.println("測試結束,執行緒為 - " + Thread.currentThread().getName());
        return "非同步寄出Email";
    }

可以在專案中輸入

http://localhost:8080/email

來感受到非同步的感覺

iShot_2023-10-08_19.50.14

非同步Executor

有時候,有時候,我們在寫的時候不能知道這東西未來會希望變成一個非同步方法,或是說希望可以用非同步的方式在程式內部實現一些功能,像這種時候我們可以結合Java的functional Interface來實現非同步的操作

faye-wong-red-beans

首先先寫一個方法介面

1
2
3
4
5
6
7
8
9
/**
 * 其實就是方法介面啦>/////<
 */
@FunctionalInterface
public interface ExecuteInterface {

    void execute();
}

接著完成AsyncTask的方法

1
2
3
4
@Async
    public void asyncExecutor(ExecuteInterface executeInterface) {
        executeInterface.execute();
    }

這樣就完成了喔!有沒有,很快吧!

當我們要在專案中非同步執行某些事情時,就可以使用這個asyncExecutor來做事了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    @GetMapping("/executor")
    public String executeAsyncTask(){
        asyncTask.asyncExecutor(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("我在做非同步工作耶!");
        });

        return "非同步執行器";
    }

iShot_2023-10-08_20.01.20

Licensed under CC BY-NC-SA 4.0