1. 程式人生 > 其它 >Spring中@Async註解使用及配置

Spring中@Async註解使用及配置

Spring中@Async註解使用及配置

參考文章:https://blog.csdn.net/weixin_42272869/article/details/123082657

一、@Async註解的使用

在使用spring框架中,可以非常簡單方便的實現一個非同步執行方法,具體只需要在啟動類新增@EnableAsync註解開啟支援非同步,然後在需要進行非同步處理的方法上使用@Async註解即可進行非同步執行。

注意:想要非同步執行,不能在一個類中直接呼叫本類中被@Async註解標記的方法,本類中直接呼叫會同步執行,不會進行非同步執行

主啟動類

@EnableAsync//開啟非同步支援,也可以標記在被@Configuration註解標註的類上,效果一致
@SpringBootApplication
public class ApplicationTest{
  ....
}

使用例項:需要交給spring容器管理bean

@Component
public class MyAsyncService {

    @Async//直接使用非同步註解即可,預設使用的執行緒池就是自定義實現的執行緒池
    public void testAsync(){
        System.out.println("==== 我執行了 ====");
        System.out.println("MyAsyncService.testAsync() = " + Thread.currentThread().getName());
    }

}

測試執行:

/* 使用SpringBoot執行測試 */
@SpringBootTest(classes = ApplicationTest.class)
public class ApplicationTest1 {

    @Resource
    MyAsyncService myAsyncService;
  
    @Test
    void testTread(){
        myAsyncService.testAsync();
        System.out.println("結束.... " );
    }
}

測試結果:

二、@Async註解執行緒池的配置及使用

1、@Async預設執行緒池修改

對於修改@Async使用的預設執行緒池,我們可以使用實現AsyncConfigurer介面,並重寫getAsyncExecutor()方法,為其提供我們自己定義的執行緒池即可

具體示例:

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 描述:執行緒池配置類,修改@Async註解預設使用的執行緒池
 *
 * @author SXT
 * @version 1.0
 * @date 2022/11/22
 */
//開啟自動啟用非同步註解,與配置類放在一起,方便管理 
@EnableAsync
@Configuration
@Slf4j
public class AsyncTaskPoolConfig implements AsyncConfigurer {

    /**
     * 用於@Async註解獲取預設執行緒連線池
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        //此類由Spring提供,org.springframework.scheduling.concurrent包下,是執行緒池的封裝類
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //執行緒池中執行緒的名字字首
        taskExecutor.setThreadNamePrefix("taskThreadPool-async-");
        //執行緒池核心執行緒數量
        taskExecutor.setCorePoolSize(5);
        //執行緒池最大執行緒數量
        taskExecutor.setMaxPoolSize(10);
        //執行緒池空閒執行緒存活時間,單位秒
        taskExecutor.setKeepAliveSeconds(100);
        //執行緒池拒絕策略
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        //執行緒池任務隊容量,如果不設定則預設 Integer.MAX_VALUE,
        // 佇列預設使用LinkedBlockingQueue 若queueCapacity的值 <= 0,則使用SynchronousQueue
        taskExecutor.setQueueCapacity(1000);

        //執行緒池中核心執行緒是否允許超時,預設為false
        taskExecutor.setAllowCoreThreadTimeOut(true);

        //執行緒池中的超時處理時間,單位秒,有一個對應方法為毫秒,預設為不超時
        taskExecutor.setAwaitTerminationSeconds(60);

        //初始化執行緒池,不可以少,否者會丟擲 執行緒池沒有初始化
        taskExecutor.initialize();
        return taskExecutor;
    }

    /**
     * 執行緒未處理異常的統一處理機制,即執行緒池異常處理器,簡單示例
     * @return
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        // 異常處理器函式介面類
        return new AsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
                log.error("============ " + throwable.getMessage() + " ===========", throwable);
                log.error("============ " + method.getName() + " ===========", objects);
            }
        };
    }
}

實現AsyncConfigurer類中的getAsyncExecutor()方法後,在使用@Async註解進行非同步執行時,預設使用的執行緒池就是實現提供的執行緒池,具體使用示例如下:

/**
 * 描述:非同步方法呼叫
 *
 * @author SXT
 * @version 1.0
 * @date 2022/11/23
 */
@Component
public class MyAsyncService {

    @Async//直接使用非同步註解即可,預設使用的執行緒池就是自定義實現的執行緒池
    public void testAsync(){
        System.out.println("==== 我執行了 ====");
        System.out.println("MyAsyncService.testAsync() = " + Thread.currentThread().getName());
    }

}

/* 使用SpringBoot執行測試 */
@SpringBootTest(classes = ApplicationTest.class)
public class ApplicationTest1 {

    @Resource
    MyAsyncService myAsyncService;
  
    @Test
    void testTread(){
        myAsyncService.testAsync();
        System.out.println("結束.... " );
    }
}

測試執行結果如下:如實使用實現提供的執行緒池

2、自定義執行緒池(@Async指定使用自定義執行緒池)

無論是修改@Async預設提供的執行緒池還是不修改,都可以對某些使用@Async標註的非同步執行方法為其指定使用具體的某一個執行緒池,若想要使用指定的執行緒池需要明確的為@Async註解指定使用的執行緒池名稱(自定義的執行緒池需要交給Spring管理),也就是bean的名稱

自定義執行緒池示例:可以看到與修改@Async預設執行緒池中提供執行緒池的內容一樣,執行緒池具體的配置可以根據需求進行設定

@Configuration
public class CommentConfig {

  /**
     * 自定義非同步執行緒池,bean的名字如果不顯示的指定,則預設使用方法的名稱作為bean的名稱
     * @return
     */
    @Bean("asyncTaskPool")
    public Executor asyncTaskPool(){
        //此類由Spring提供,org.springframework.scheduling.concurrent包下,是執行緒池的封裝類
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //執行緒池中執行緒的名字字首
        executor.setThreadNamePrefix("asyncTaskPool-task-");
        //執行緒池核心執行緒數量
        executor.setCorePoolSize(5);
        //執行緒池最大執行緒數量
        executor.setMaxPoolSize(10);
        //執行緒池空閒執行緒存活時間,單位秒
        executor.setKeepAliveSeconds(100);
        //執行緒池拒絕策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //執行緒池任務隊容量,如果不設定則預設 Integer.MAX_VALUE,
        // 佇列預設使用LinkedBlockingQueue 若queueCapacity的值 <= 0,則使用SynchronousQueue
        executor.setQueueCapacity(1000);

        //執行緒池中核心執行緒是否允許超時,預設為false
        executor.setAllowCoreThreadTimeOut(true);

        //執行緒池中的超時處理時間,單位秒,有一個對應方法為毫秒,預設為不超時
        executor.setAwaitTerminationSeconds(60);

        //初始化執行緒池,不可以少,否者會丟擲 執行緒池沒有初始化
        executor.initialize();
        return executor;
    }

}

使用自定義執行緒池示例:

/**
 * 描述:非同步方法呼叫
 *
 * @author SXT
 * @version 1.0
 * @date 2022/11/23
 */
@Component
public class MyAsyncService {
		
  	//指定使用執行緒池的bean的名稱,不指定的話使用的是預設提供的執行緒池
    //asyncTaskPool就是被Spring管理的執行緒池例項的物件名稱
    @Async("asyncTaskPool")
    public void testAsync2(){
        System.out.println("==== 我執行了 ====");
        System.out.println("MyAsyncService.testAsync2() = " + Thread.currentThread().getName());
    }

}


/* 使用SpringBoot執行測試 */
@SpringBootTest(classes = ApplicationTest.class)
public class ApplicationTest1 {

    @Resource
    MyAsyncService myAsyncService;
  
    @Test
    void testTread2(){
        myAsyncService.testAsync2();
        System.out.println("結束.... " );
    }
}

測試結果如下:

拓展:

若想要根據配置檔案進行動態的配置自定義執行緒池,則可以使用如下方式

配置檔案properties檔案或yml檔案

#這裡使用properties檔案進行配置,yml檔案同樣的方式,只是格式不同
#也可以將駝峰命名改成core-size ,keep-alive-seconds,對應的實體類依舊是駝峰命名
asyncTask.pool.coreSize=5
asyncTask.pool.maxPoolSize=10
asyncTask.pool.keepAliveSeconds=60
asyncTask.pool.queueCapacity=1000
asyncTask.pool.timeOutSeconds=60

定義獲取配置檔案對應配置的類

/**
 * 描述:執行緒池配置檔案實體類
 *
 * @author SXT
 * @version 1.0
 * @date 2022/11/23
 */
//使用@ConfigurationProperties註解,其類必須交給Spring管理
@ConfigurationProperties(prefix = "async-task.pool")
@Component
@ToString
@Data
public class ThreadConfig {

    private int coreSize;

    private int maxPoolSize;

    private long keepAliveSeconds;

    private int queueCapacity;

    private long timeOutSeconds;

}

@SpringBootTest(classes = ApplicationTest.class)
public class ApplicationTest1 {

    @Autowired
    ThreadConfig threadConfig;

    @Test
    void testProperties1(){
        System.out.println("threadConfig = " + threadConfig);

    }
}

測試結果:

提示:配置的key不要寫錯,不然獲取不到值

三、@Async註解的原理

待完善