Spring非同步詳解

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

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-15 16:28
下一篇 2024-12-15 16:28

相關推薦

發表回復

登錄後才能評論