在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,可以看到"非同步測試執行,執行緒為:這句話打印出來的時間點,遠小於測試結束,執行緒為 - 這句話,這就是非同步執行帶來的好處。

繼續感受非同步執行
之前在資策會的專案中,有一個功能是要寄出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
來感受到非同步的感覺

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

首先先寫一個方法介面
| 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 "非同步執行器";
    }
 | 
