首頁  >  文章  >  Java  >  SpringBoot怎麼優雅地實現非同步調用

SpringBoot怎麼優雅地實現非同步調用

WBOY
WBOY轉載
2023-05-12 22:04:041715瀏覽

前言

  • 同步程式設計:在同步程式設計中,任務一次執行一個,只有當一個任務完成時,下一個任務才會被解除阻塞。

  • 非同步程式設計:在非同步程式設計中,可以同時執行多個任務。您可以在上一個任務完成之前轉到另一個任務。

SpringBoot怎麼優雅地實現非同步調用

Spring Boot#中,我們可以使用@Async註解來實現異步行為。

實作步驟

1.定義一個非同步服務介面AsyncService.java

public interface AsyncService {

    void asyncMethod() throws InterruptedException;

    Future<String> futureMethod() throws InterruptedException;
}

2.實作定義的介面AsyncServiceImpl.java

@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService  {

    @Async
    @Override
    public void asyncMethod() throws InterruptedException {
        Thread.sleep(3000);
        log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
    }

    @Async
    @Override
    public Future<String> futureMethod() throws InterruptedException {
        Thread.sleep(5000);
        log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
        return new AsyncResult<>("task Done");
    }
}
  • AsyncServiceImpl 是一個 spring 管理的 bean

  • 您的非同步方法必須是公共的,而且是被@Async註解修飾。

  • 回傳類型被限制為 void 或 Future

3.定義一個控制器AsyncController.java

#
@EnableAsync
@RestController
@Slf4j
public class AsyncController {
    @Autowired
    AsyncService asyncService;

    @GetMapping("/async")
    public String asyncCallerMethod() throws InterruptedException {
        long start = System.currentTimeMillis();
        log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
        asyncService.asyncMethod();
        String response = "task completes in :" +
                (System.currentTimeMillis() - start) + "milliseconds";
        return response;
    }

    @GetMapping("/asyncFuture")
    public String asyncFuture() throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
        Future<String> future = asyncService.futureMethod();
        // 阻塞获取结果
        String taskResult = future.get();
        String response = taskResult + "task completes in :" +
                (System.currentTimeMillis() - start) + "milliseconds";
        return response;
    }
}
  • 關鍵點,需要新增啟用異步的註解@EnableAsync ,當然這個註解加在其他地方也ok得。

  • 當外部呼叫該介面時,asyncMethod()將由預設任務執行程式建立的另一個執行緒執行,主執行緒不需要等待完成非同步方法執行。

4.運行一下

現在我們執行一下看看,是不是非同步回傳的。

SpringBoot怎麼優雅地實現非同步調用

SpringBoot怎麼優雅地實現非同步調用

可以看到呼叫/async接口,最終一步呼叫了方法。

SpringBoot怎麼優雅地實現非同步調用

SpringBoot怎麼優雅地實現非同步調用

呼叫/asyncFuture,發現回傳5秒多,難道不是非同步的嗎?其實也是異步的,看日誌可以看出來,只不過我們回傳的是Future,呼叫Futrue.get()是阻塞的。

自訂非同步任務執行器和例外處理

我們現在看看如果異常方法中報錯了會怎麼樣?修改非同步程式碼如下所示,會拋運行時異常:

SpringBoot怎麼優雅地實現非同步調用

再次執行非同步接口,如下所示,會使用預設的執行緒池和異常處理。

SpringBoot怎麼優雅地實現非同步調用

我們也可以自訂非同步方法的處理例外和非同步任務執行器,我們需要設定 AsyncUncaughtExceptionHandler,如下程式碼所示:

@Configuration
public class AsynConfiguration extends AsyncConfigurerSupport {
   @Override
   public Executor getAsyncExecutor() {
      ThreadPoolTaskExecutor executor = new 
                ThreadPoolTaskExecutor();
      executor.setCorePoolSize(3);
      executor.setMaxPoolSize(4);
      executor.setThreadNamePrefix("asyn-task-thread-");
      executor.setWaitForTasksToCompleteOnShutdown(true);
      executor.initialize();
      return executor;
  }
  @Override
  public AsyncUncaughtExceptionHandler  
         getAsyncUncaughtExceptionHandler() {
     return new AsyncUncaughtExceptionHandler() {
   
        @Override
        public void handleUncaughtException(Throwable ex, 
           Method method, Object... params) {
           System.out.println("Exception: " + ex.getMessage());
           System.out.println("Method Name: " + method.getName());
           ex.printStackTrace();
        }
    };
  }
}

再次運行,得到的結果如下:

SpringBoot怎麼優雅地實現非同步調用

@Async如何運作的

必須透過使用 @EnableAsync註解註解主應用程式類別或任何直接或間接非同步方法呼叫程式類別來啟用非同步支援。主要透過代理模式實現,預設模式是 Proxy,另一種是 AspectJ。代理模式只允許透過代理攔截呼叫。永遠不要從定義它的同一個類別呼叫非同步方法,它不會起作用。

當使用 @Async對方法進行註解時,它會根據「proxyTargetClass」屬性為該物件建立一個代理程式。當 spring 執行這個方法時,預設情況下它會搜尋關聯的執行緒池定義。上下文中唯一的 spring 框架 TaskExecutor bean 或名為「taskExecutor」的 #Executor bean。如果這兩者都不可解析,預設會使用spring框架SimpleAsyncTaskExecutor來處理非同步方法的執行。

以上是SpringBoot怎麼優雅地實現非同步調用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除