Spring框架作為一個全面的企業級框架,提供了強大的非同步功能,讓開發者可以更方便地處理高並發場景。本文將從多個方面詳細介紹Spring非同步的相關知識。
一、Spring非同步方法
Spring允許我們以非同步的方式執行方法,這樣可以在方法執行期間使應用程序繼續響應其他請求。這對於在高負載情況下處理請求非常有用。Spring非同步方法主要有兩種實現方式:基於註解和基於XML配置。
基於註解的方式
使用Spring基於註解的非同步方法很容易。只需用@Async註解標記方法即可,如下代碼:
@Component
public class AsyncTestServiceImpl implements AsyncTestService {
@Override
@Async
public void asyncMethod() {
// 非同步方法邏輯
}
}
這樣,Spring就會在非同步情況下執行asyncMethod()方法,而不會阻塞主線程或其他線程的執行。需要在配置文件中開啟非同步功能:
在上面的代碼中,我們使用了配置開啟非同步功能,並指定了一個線程池(pool-size=”5″)。Spring在非同步調用時會自動獲取該線程池的線程執行非同步方法。
基於XML配置的方式
在基於XML的配置中,需加入配置項指定線程池,。然後,需要將非同步類定義為Spring bean的一個實例,並將非同步執行的方法名稱作為元素的屬性值進行配置,如下代碼:
二、Spring非同步執行及監控
非同步執行通常需要在多線程環境下進行,因此任務的執行情況需要進行監控。Spring提供了TaskExecutor和TaskScheduler等介面來執行非同步任務並監控。TaskExecutor介面用於非同步執行任務,而TaskScheduler介面用於基於時間的任務調度。Spring還提供了AsyncUncaughtExceptionHandler介面來管理非同步任務拋出的異常。
非同步執行
TaskExecutor是Spring框架提供的一個介面,用於非同步執行任務。下面是一段TaskExecutor的示例代碼:
@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {
@Bean(name = "asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(20);
executor.setThreadNamePrefix("asyncExecutor-");
executor.initialize();
return executor;
}
}
上面的代碼將創建一個ThreadPoolTaskExecutor對象,非同步執行的線程池大小為5,最大線程池大小為10,隊列容量為20。
非同步監控
要監控非同步執行情況,可以使用AsyncTaskExecutor類。以下是一個示例代碼:
@Component("asyncTaskExecutor")
public class AsyncTaskExecutor implements TaskExecutor {
private TaskExecutor taskExecutor;
@Override
public void execute(Runnable task) {
// 非同步執行線程,監控線程執行情況
taskExecutor.execute(task);
}
/**
* 開啟非同步任務
*
* @param task 任務
* @param startAtDelay 延時時間
* @param timeout 處理超時時間
*/
public void execute(final Runnable task, final long startAtDelay, final long timeout) {
// 非同步執行線程,監控線程執行情況
taskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(startAtDelay);
} catch (InterruptedException e) {
}
task.run();
}
}, timeout);
}
}
三、Spring非同步調用
Spring允許我們以非同步方式調用方法。這樣可以在應用程序正常響應請求的同時,處理其他時間密集型操作。例如:郵件發送、簡訊發送等待等會佔用大量時間的操作。Spring提供了兩種方式來實現非同步調用。
基於註解的方式
使用Spring的非同步調用註解很容易,只需使用@Async註解標記調用非同步方法的方法即可。下面的代碼示例展示了使用@Async來非同步調用SendEmailService:
@Service
public class AsyncEmailServiceImpl implements AsyncEmailService {
@Autowired
private SendEmailService sendEmailService;
@Override
@Async
public void sendEmail() {
try {
sendEmailService.sendEmail();
} catch (Exception e) {
log.error("Send email error: {}", e.getMessage(), e);
}
}
}
public interface SendEmailService {
void sendEmail() throws Exception;
}
@Service
public class SendEmailServiceImpl implements SendEmailService {
@Override
public void sendEmail() throws Exception {
// 發送郵件邏輯
}
}
基於XML配置的方式
在基於XML的配置方式中,需要使用配置非同步任務執行線程池,例如:
除此之外,由於非同步調用可能會出現異常,我們需要將AsyncUncaughtExceptionHandler類的實現注入Spring容器中,如下所示:
@Bean
public AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("Method [{}] throwing [{}]", method.getName(), throwable.getMessage(), throwable);
}
}
四、Spring非同步線程池
Spring的非同步處理依賴於線程池。Spring為我們提供了三種線程池:
- SimpleAsyncTaskExecutor:每次調用都會創建一個新的線程。
- SyncTaskExecutor:同步執行任務,不會創建新的線程。
- ThreadPoolTaskExecutor:使用線程池來非同步執行任務。
ThreadPoolTaskExecutor是最常用的,因為線程池可以重用線程,減少了線程的創建和銷毀的開銷。在ThreadPoolTaskExecutor的配置中,我們可以指定線程池的核心線程數、最大線程數和線程的存活時間。
五、Spring非同步編排
Spring提供了非同步編排功能,可以將非同步任務進行編排,提高運行效率。Spring非同步編排依賴於@Async和CompletableFuture兩個註解。
使用@Async註解編排任務
下面的代碼示例展示了如何使用@Async註解編排任務:
@Component
public class AsyncTestServiceImpl implements AsyncTestService {
@Async
public Future taskOne() {
// 第一個任務邏輯
return new AsyncResult("taskOne");
}
@Async
public Future taskTwo() {
// 第二個任務邏輯
return new AsyncResult("taskTwo");
}
@Async
public Future taskThree(String param) {
// 第三個任務邏輯
return new AsyncResult("taskThree" + param);
}
}
@Component
public class AppComponent {
@Autowired
private AsyncTestServiceImpl asyncTestService;
public void execute() throws InterruptedException, ExecutionException {
Future f1 = asyncTestService.taskOne();
Future f2 = asyncTestService.taskTwo();
Future f3 = asyncTestService.taskThree(" param");
while (!(f1.isDone() && f2.isDone() && f3.isDone())) {
Thread.sleep(10);
}
System.out.println(f1.get());
System.out.println(f3.get());
System.out.println(f2.get());
}
}
在上面的代碼中,我們使用@Async註解標記了三個任務,並使用Future來獲取執行結果,最後通過while循環等待任務執行完成並獲取結果。
使用CompletableFuture編排任務
下面的代碼示例展示了如何使用CompletableFuture編排任務:
@Component
public class AsyncTestServiceImpl {
@Async
public CompletableFuture taskOne() {
// 第一個任務邏輯
return CompletableFuture.completedFuture("taskOne");
}
@Async
public CompletableFuture taskTwo() {
// 第二個任務邏輯
return CompletableFuture.completedFuture("taskTwo");
}
@Async
public CompletableFuture taskThree(String param) {
// 第三個任務邏輯
return CompletableFuture.completedFuture("taskThree" + param);
}
}
@Component
public class AppComponent {
@Autowired
private AsyncTestServiceImpl asyncTestService;
public CompletableFuture execute() {
CompletableFuture f1 = asyncTestService.taskOne();
CompletableFuture f2 = asyncTestService.taskTwo();
return f1
.thenCompose(result1 -> asyncTestService.taskThree(result1))
.thenCombine(f2, (result3, result2) -> result2 + result3)
.exceptionally(e -> "exception");
}
}
在上面的代碼中,我們使用CompletableFuture來編排三個任務,其中thenCompose和thenCombine方法用於非同步執行任務並組合每個任務的結果。返回結果時,我們可以使用exceptionally方法捕獲異常並返回相應結果。
六、Spring非同步事件
Spring提供了非同步事件機制,可以在應用程序內部處理各種不同的事件。非同步事件通過ApplicationEventPublisher和ApplicationListener兩個介面實現。其中,ApplicationEventPublisher介面是用於發布事件的,ApplicationListener介面則是用於監聽事件並做出響應的。
定義非同步事件
下面是定義非同步事件的示例代碼:
public class EmailEvent extends ApplicationEvent {
public EmailEvent(Object source) {
super(source);
}
public String getMessage() {
return "email event";
}
}
上面的代碼定義了一個EmailEvent,該事件繼承自ApplicationEvent類,包含了一條消息。
發布非同步事件
下面是發布非同步事件的示例代碼:
@Service
public class EmailSendService {
@Autowired
private ApplicationEventPublisher publisher;
public void sendEmail() {
// 發送郵件邏輯
EmailEvent event = new EmailEvent(this);
publisher.publishEvent(event);
}
}
在上面的代碼中,我們使用ApplicationEventPublisher來發布一個EmailEvent事件。
監聽非同步事件並做出響應
下面是監聽非同步事件並做出響應的示例代碼:
@Component
public class EmailEventListener {
@EventListener
public void handleEmailEvent(EmailEvent emailEvent) {
// 處理郵件事件
System.out.println(emailEvent.getMessage());
}
}
在上面的代碼中,我們使用@EventListener注
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/259252.html