溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

SpringBoot多線程@Async怎么用

發(fā)布時(shí)間:2021-12-29 12:39:44 來(lái)源:億速云 閱讀:150 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了SpringBoot多線程@Async怎么用,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

    多線程@Async的使用體驗(yàn)

    場(chǎng)景

    導(dǎo)入:可以將大批量的數(shù)據(jù)insert操作采用多線程的方式并行執(zhí)行

    第三方服務(wù)的接口調(diào)用:由于存在個(gè)別第三方服務(wù)調(diào)用比較耗時(shí)的場(chǎng)景,此時(shí)就可以與自身服務(wù)的邏輯并行執(zhí)行

    簡(jiǎn)而言之:接口中部份業(yè)務(wù)邏輯可以通過(guò)并行的方式來(lái)優(yōu)化接口性能

    1.線程池配置

    @Configuration
    @EnableAsync
    public class TaskPoolConfig {
    
        @Bean("taskExecutor") // bean 的名稱,默認(rèn)為首字母小寫(xiě)的方法名
        public Executor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //核心線程數(shù)(CPU核心數(shù)+1)
            executor.setCorePoolSize(10);
            //最大線程數(shù)(2*CPU核心數(shù)+1)
            executor.setMaxPoolSize(20);
            //緩沖隊(duì)列數(shù)
            executor.setQueueCapacity(200);
            //允許線程空閑時(shí)間(單位:默認(rèn)為秒)
            executor.setKeepAliveSeconds(60);
            //線程池名前綴
            executor.setThreadNamePrefix("sub-thread-");
            // 增加 TaskDecorator 屬性的配置
            executor.setTaskDecorator(new ContextDecorator());
            // 線程池對(duì)拒絕任務(wù)的處理策略:CallerRunsPolicy:不在新線程中執(zhí)行任務(wù),而是由調(diào)用者所在的線程來(lái)執(zhí)行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.initialize();
            return executor;
        }
    }

    2.子父線程之間共享一個(gè)Request的配置方案

    1.實(shí)現(xiàn)TaskDecorator接口

    /**
     * 子線程裝飾器
     *
     * @author Da Shuai
     * @date 2021-06-10 18:28:17
     */
    public class SubThreadTaskDecorator implements TaskDecorator {
    
        @Override
        public Runnable decorate(Runnable runnable) {
            RequestAttributes context = RequestContextHolder.currentRequestAttributes();
            return () -> {
                try {
                    RequestContextHolder.setRequestAttributes(context);
                    runnable.run();
                } finally {
                    RequestContextHolder.resetRequestAttributes();
                }
            };
        }
    }

    2.之前的線程池配置加如下代碼使其生效

    // 增加 TaskDecorator 屬性的配置
    executor.setTaskDecorator(new ContextDecorator());

    3.阻塞主線程,等待所有子線程執(zhí)行完畢后繼續(xù)執(zhí)行主線程

    1.CountDownLatch

    思路:

    • 實(shí)例化CountDownLatch對(duì)象,同時(shí)傳入x(線程數(shù)量:這個(gè)數(shù)量必須等于子線程數(shù)量)進(jìn)行構(gòu)造

    • 每個(gè)子線程執(zhí)行完畢后會(huì)調(diào)用countDown()方法

    • 子線程邏輯后方調(diào)用await()方法

    這樣線程計(jì)數(shù)器為0之前,主線程就一直處于pending狀態(tài)

    主線程邏輯

    new CountDownLatch(X)
    latch.await()
    @Override
    @Transactional
    public void importExcel(File file) {
        CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            VoteDO voteDO = new VoteDO();
            voteDO.setTitle(i + "");
            asyncManager.asyncSaveVote(voteDO);
        }
        //System.out.println(1/0);
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    子線程邏輯

    latch.countDown()
    @Override
    @Async
    public void asyncSaveVote(VoteDO voteDO, CountDownLatch latch) {
        log.info("當(dāng)前線程為 {},休眠10s開(kāi)始", Thread.currentThread().getName());
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("當(dāng)前線程為 {},休眠10s結(jié)束", Thread.currentThread().getName());
        log.info("當(dāng)前線程為 {},保存開(kāi)始", Thread.currentThread().getName());
        voteDO.setDesc(Thread.currentThread().getName());
        voteDao.insert(voteDO);
        latch.countDown();
        log.info("當(dāng)前線程為 {},保存結(jié)束", Thread.currentThread().getName());
    }

    日志

    2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : ===============請(qǐng)求內(nèi)容===============
    2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 請(qǐng)求地址:http://localhost:8080/api/import
    2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 請(qǐng)求方式:POST
    2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 請(qǐng)求類方法:com.zhdj.controller.ImportController.importExcel
    2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 請(qǐng)求類方法參數(shù):[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@42c3f403]
    2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : ===============請(qǐng)求內(nèi)容===============
    2021-06-11 16:31:08.676 INFO 27516 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
    2021-06-11 16:31:08.894 INFO 27516 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
    2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-3,休眠10s開(kāi)始
    2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-1,休眠10s開(kāi)始
    2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-2,休眠10s開(kāi)始
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-2,休眠10s結(jié)束
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-3,休眠10s結(jié)束
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-2,保存開(kāi)始
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-1,休眠10s結(jié)束
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-3,保存開(kāi)始
    2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-1,保存開(kāi)始
    2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : ==> Parameters: 0(String), sub-thread-1(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
    2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : ==> Parameters: 2(String), sub-thread-3(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
    2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : ==> Parameters: 1(String), sub-thread-2(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
    2021-06-11 16:31:19.172 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:31:19.178 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:31:19.187 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-3,保存結(jié)束
    2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-1,保存結(jié)束
    2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-2,保存結(jié)束
    2021-06-11 16:31:19.226 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : --------------返回內(nèi)容----------------
    2021-06-11 16:31:19.328 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : Response內(nèi)容:null
    2021-06-11 16:31:19.328 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : --------------返回內(nèi)容----------------

    2.Future

    思路:

    1.子線程邏輯返回Future對(duì)象

    2.主線程邏輯循環(huán)判斷每個(gè)子線程返回的Future對(duì)象isDone()是否為true

    主線程邏輯

    循環(huán)判斷future.isDone()是否為true

    @Override
    @Transactional
    public void importExcel(File file) {
     List<Future> futureList = new ArrayList<>();
     for (int i = 0; i < 3; i++) {
         VoteDO voteDO = new VoteDO();
         voteDO.setTitle(i + "");
         Future future = asyncManager.asyncSaveVote(voteDO);
         futureList.add(future);
     }
     //檢查所有子線程是否均執(zhí)行完畢
     while (true) {
         boolean isAllDone = true;
         for (Future future : futureList) {
             if (null == future || !future.isDone()) {
                 isAllDone = false;
             }
         }
         if (isAllDone) {
             log.info("所有子線程執(zhí)行完畢");
             break;
         }
     }
    }

    子線程邏輯

    返回Future對(duì)象

    @Override
    public Future asyncSaveVote(VoteDO voteDO) {
        log.info("當(dāng)前線程為 {},休眠10s開(kāi)始", Thread.currentThread().getName());
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("當(dāng)前線程為 {},休眠10s結(jié)束", Thread.currentThread().getName());
        log.info("當(dāng)前線程為 {},保存開(kāi)始", Thread.currentThread().getName());
        voteDO.setDesc(Thread.currentThread().getName());
        voteDao.insert(voteDO);
        log.info("當(dāng)前線程為 {},保存結(jié)束", Thread.currentThread().getName());
        //返回需要用AsyncResult類
        return new AsyncResult<>(true);
    }

    日志

    2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : ===============請(qǐng)求內(nèi)容===============
    2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 請(qǐng)求地址:http://localhost:8080/api/import
    2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 請(qǐng)求方式:POST
    2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 請(qǐng)求類方法:com.zhdj.controller.ImportController.importExcel
    2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 請(qǐng)求類方法參數(shù):[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@7e23bacc]
    2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : ===============請(qǐng)求內(nèi)容===============
    2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-5,休眠10s開(kāi)始
    2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-4,休眠10s開(kāi)始
    2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-6,休眠10s開(kāi)始
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-6,休眠10s結(jié)束
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-4,休眠10s結(jié)束
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-5,休眠10s結(jié)束
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-6,保存開(kāi)始
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-5,保存開(kāi)始
    2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-4,保存開(kāi)始
    2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
    2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : ==> Parameters: 1(String), sub-thread-5(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
    2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : ==> Parameters: 0(String), sub-thread-4(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
    2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : ==> Parameters: 2(String), sub-thread-6(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
    2021-06-11 16:42:38.988 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:42:38.989 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-5,保存結(jié)束
    2021-06-11 16:42:38.993 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:42:38.993 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-6,保存結(jié)束
    2021-06-11 16:42:39.004 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : <== Updates: 1
    2021-06-11 16:42:39.005 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 當(dāng)前線程為 sub-thread-4,保存結(jié)束
    2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.service.impl.VoteServiceImpl : 所有子線程執(zhí)行完畢
    2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : --------------返回內(nèi)容----------------
    2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : Response內(nèi)容:null
    2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : --------------返回內(nèi)容----------------

    4.多線程共用一個(gè)事務(wù)

    暫時(shí)無(wú)解決方案,這是弊端

    異步調(diào)用@Async問(wèn)題

    1.使用背景

    在項(xiàng)目中,當(dāng)訪問(wèn)其他人的接口較慢或者做耗時(shí)任務(wù)時(shí),不想程序一直卡在耗時(shí)任務(wù)上,想程序能夠并行執(zhí)行,我們可以使用多線程來(lái)并行的處理任務(wù),也可以使用spring提供的異步處理方式@Async。

    2.異步處理方式

    調(diào)用之后,不返回任何數(shù)據(jù)。

    調(diào)用之后,返回?cái)?shù)據(jù),通過(guò)Future來(lái)獲取返回?cái)?shù)據(jù)

    3.@Async不返回?cái)?shù)據(jù)

    使用@EnableAsync啟用異步注解

    @Configuration
    @EnableAsync
    @Slf4j
    public class AsyncConfig{
    }

    在異步處理的方法dealNoReturnTask上添加注解@Async

    @Component
    @Slf4j
    public class AsyncTask {
    
        @Async
        public void dealNoReturnTask(){
            log.info("Thread {} deal No Return Task start", Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Thread {} deal No Return Task end at {}", Thread.currentThread().getName(), System.currentTimeMillis());
        }
    }

    Test測(cè)試類:

    @SpringBootTest(classes = SpringbootApplication.class)
    @RunWith(SpringJUnit4ClassRunner.class)
    @Slf4j
    public class AsyncTest {
        @Autowired
        private AsyncTask asyncTask;
    
        @Test
        public void testDealNoReturnTask(){
            asyncTask.dealNoReturnTask();
            try {
                log.info("begin to deal other Task!");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    日志打印結(jié)果為:

    begin to deal other Task!
    AsyncExecutorThread-1 deal No Return Task start
    AsyncExecutorThread-1 deal No Return Task end at 1499751227034

    從日志中我們可以看出,方法dealNoReturnTask()是異步執(zhí)行完成的。

    dealNoReturnTask()設(shè)置sleep 3s是為了模擬耗時(shí)任務(wù)

    testDealNoReturnTask()設(shè)置sleep 10s是為了確認(rèn)異步是否執(zhí)行完成

    4.@Async返回?cái)?shù)據(jù)

    異步調(diào)用返回?cái)?shù)據(jù),F(xiàn)uture表示在未來(lái)某個(gè)點(diǎn)獲取執(zhí)行結(jié)果,返回?cái)?shù)據(jù)類型可以自定義

    @Async
        public Future<String> dealHaveReturnTask() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("thread", Thread.currentThread().getName());
            jsonObject.put("time", System.currentTimeMillis());
            return new AsyncResult<String>(jsonObject.toJSONString());
        }

    測(cè)試類用isCancelled判斷異步任務(wù)是否取消,isDone判斷任務(wù)是否執(zhí)行結(jié)束

    @Test
        public void testDealHaveReturnTask() throws Exception {
    
            Future<String> future = asyncTask.dealHaveReturnTask();
            log.info("begin to deal other Task!");
            while (true) {
                if(future.isCancelled()){
                    log.info("deal async task is Cancelled");
                    break;
                }
                if (future.isDone() ) {
                    log.info("deal async task is Done");
                    log.info("return result is " + future.get());
                    break;
                }
                log.info("wait async task to end ...");
                Thread.sleep(1000);
            }
        }

    日志打印如下,我們可以看出任務(wù)一直在等待異步任務(wù)執(zhí)行完畢,用future.get()來(lái)獲取異步任務(wù)的返回結(jié)果

    begin to deal other Task!
    wait async task to end ...
    wait async task to end ...
    wait async task to end ...
    wait async task to end ...
    deal async task is Done
    return result is {"thread":"AsyncExecutorThread-1","time":1499752617330}

    5.異常處理

    我們可以實(shí)現(xiàn)AsyncConfigurer接口,也可以繼承AsyncConfigurerSupport類來(lái)實(shí)現(xiàn)

    在方法getAsyncExecutor()中創(chuàng)建線程池的時(shí)候,必須使用 executor.initialize(),

    不然在調(diào)用時(shí)會(huì)報(bào)線程池未初始化的異常。

    如果使用threadPoolTaskExecutor()來(lái)定義bean,則不需要初始化

    @Configuration
    @EnableAsync
    @Slf4j
    public class AsyncConfig implements AsyncConfigurer {
    
    //    @Bean
    //    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
    //        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    //        executor.setCorePoolSize(10);
    //        executor.setMaxPoolSize(100);
    //        executor.setQueueCapacity(100);
    //        return executor;
    //    }
    
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10);
            executor.setMaxPoolSize(100);
            executor.setQueueCapacity(100);
            executor.setThreadNamePrefix("AsyncExecutorThread-");
            executor.initialize(); //如果不初始化,導(dǎo)致找到不到執(zhí)行器
            return executor;
        }
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return new AsyncExceptionHandler();
        }
    }

    異步異常處理類:

    @Slf4j
    public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.info("Async method: {} has uncaught exception,params:{}", method.getName(), JSON.toJSONString(params));
    
            if (ex instanceof AsyncException) {
                AsyncException asyncException = (AsyncException) ex;
                log.info("asyncException:{}",asyncException.getErrorMessage());
            }
    
            log.info("Exception :");
            ex.printStackTrace();
        }
    }

    異步處理異常類:

    @Data
    @AllArgsConstructor
    public class AsyncException extends Exception {
        private int code;
        private String errorMessage;
    }

    在無(wú)返回值的異步調(diào)用中,異步處理拋出異常,AsyncExceptionHandler的handleUncaughtException()會(huì)捕獲指定異常,原有任務(wù)還會(huì)繼續(xù)運(yùn)行,直到結(jié)束。

    在有返回值的異步調(diào)用中,異步處理拋出異常,會(huì)直接拋出異常,異步任務(wù)結(jié)束,原有處理結(jié)束執(zhí)行。

    感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“SpringBoot多線程@Async怎么用”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI