当前位置: 代码网 > it编程>编程语言>Java > Springboot加@Async注解异步线程执行启动报错问题及解决过程

Springboot加@Async注解异步线程执行启动报错问题及解决过程

2026年09月07日 Java 我要评论
背景方法需要异步执行所以在需要的方法上添加了@async,启动类添加了@enableasync但碰到以下情况1、注解无效原因:我在调用的方法是写在同一个类下的,类似下面的所以不行为此我将方法写在其他接

背景

方法需要异步执行所以在需要的方法上添加了@async,启动类添加了@enableasync但碰到以下情况

1、注解无效

原因:我在调用的方法是写在同一个类下的,类似下面的所以不行

为此我将方法写在其他接口中引出了第二个问题

2、启动报错

原因找了好久是因为springboot是jdk动态代理,因为有实现类(imp)那必须得注入接口类(service层)通过接口类去引用实现的方法,但是还是报错原因居然是这样:

该实现层(imp)在其他地方有被直接注入(如

@autowired
private doccheckserviceimpl doccheckserviceimpl;

那就不行了),所以其他地方改成接口类去实现其方法,启动就正常了

重点来了

如果你在实际项目开发中真这样写了,肯定会被老鸟们无情嘲讽

 因为一般的代码忽略了一个最大的问题,就是给@async异步框架自定义线程池,

使用@async注解,在默认情况下用的是simpleasynctaskexecutor线程池,该线程池不是真正意义上的线程池。

使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发outofmemoryerror错误。

正因如此,所以我们在使用spring中的@async异步框架时一定要自定义线程池,替代默认的simpleasynctaskexecutor

spring提供了多种线程池:

  • simpleasynctaskexecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  • synctaskexecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地
  • concurrenttaskexecutor:executor的适配类,不推荐使用。如果threadpooltaskexecutor不满足要求时,才用考虑使用这个类
  • threadpooltaskscheduler:可以使用cron表达式
  • threadpooltaskexecutor :最常使用,推荐。其实质是对java.util.concurrent.threadpoolexecutor的包装

为@async实现一个自定义线程池:

@configuration
@enableasync
public class syncconfiguration {
    @bean(name = "asyncpooltaskexecutor")
    public threadpooltaskexecutor executor() {
        threadpooltaskexecutor taskexecutor = new threadpooltaskexecutor();
        //核心线程数
        taskexecutor.setcorepoolsize(10);
        //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        taskexecutor.setmaxpoolsize(100);
        //缓存队列
        taskexecutor.setqueuecapacity(50);
        //许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        taskexecutor.setkeepaliveseconds(200);
        //异步方法内部线程名称
        taskexecutor.setthreadnameprefix("async-");
        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumpoolsize,如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略:
         * threadpoolexecutor.abortpolicy:丢弃任务并抛出rejectedexecutionexception异常。
         * threadpoolexecutor.discardpolicy:也是丢弃任务,但是不抛出异常。
         * threadpoolexecutor.discardoldestpolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         * threadpoolexecutor.callerrunspolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
         */
        taskexecutor.setrejectedexecutionhandler(new threadpoolexecutor.callerrunspolicy());
        taskexecutor.initialize();
        return taskexecutor;
    }
}

配置自定义线程池以后我们就可以大胆的使用@async提供的异步处理能力了。

为@async指定线程池名字

@sneakythrows
@async("asyncpooltaskexecutor")
public void dotask1() {
  long t1 = system.currenttimemillis();
  thread.sleep(2000);
  long t2 = system.currenttimemillis();
  log.info("task1 cost {} ms" , t2-t1);
}

1、个人使用的示例1(等待全部执行完)

    /**
     * 异步更新值域
     */

       executorservice executor = new threadpoolexecutor(
            2,//核心线程数
            5,//最大线程数
            3,//线程空闲时间
            timeunit.seconds,//时间单位
            new linkedblockingdeque<>(3),
            executors.defaultthreadfactory(),
            new threadpoolexecutor.discardoldestpolicy());


    @override
    @async("asyncpooltaskexecutor")
    public void updaterange() {
        string lockkey = "rang_lock_key";
        string lockvalue = "rang_lock_value";
        final boolean lock = redislock.trylock(lockkey, lockvalue, checkconstant.check_lock_time);
        if (!lock) {
            throw new baseexception("值域更新进行中...请稍后再试!");
        }

        map<string, integer> countmap = new hashmap<>(16);
        integer count = 0;
        // 先查询rp_md_range 表中最新的时间
        final list<string> maxtime = basemapper.selectmaxtime();
        date lastupdatetime = new date();
        boolean iseffectivetime = collectionutil.isnotempty(maxtime) && strutil.isnotblank(maxtime.get(0));
        if (iseffectivetime) {
            lastupdatetime = datetimeutil.formattime(maxtime.get(0));
        }
        long now = system.currenttimemillis();
        log.info("值域开始更新值域:{}", now);
        try {
            date finallastupdatetime = lastupdatetime;
            // 字典类型
            completablefuture<void> dictfuture = completablefuture.runasync(() -> getdict(countmap, finallastupdatetime, iseffectivetime), executor);

            // 药品
            completablefuture<void> medicinefuture = completablefuture.runasync(() -> getmedicine(countmap, finallastupdatetime, iseffectivetime), executor);
            // 医疗服务
            completablefuture<void> serverfuture = completablefuture.runasync(() -> getserver(countmap, finallastupdatetime, iseffectivetime), executor);
            // 手术
            completablefuture<void> icdcmfuture = completablefuture.runasync(() -> geticdcm(countmap, finallastupdatetime, iseffectivetime), executor);
            // 疾病
            completablefuture<void> icdfuture = completablefuture.runasync(() -> geticd(countmap, finallastupdatetime, iseffectivetime), executor);
            // 中医疾病
            completablefuture<void> icdcnfuture = completablefuture.runasync(() -> geticdcn(countmap, finallastupdatetime, iseffectivetime), executor);
            // 中医症候
            completablefuture<void> icdcntypefuture = completablefuture.runasync(() -> geticdcntype(countmap, finallastupdatetime, iseffectivetime), executor);

            // 等待全部执行完
            completablefuture.allof(dictfuture, medicinefuture, serverfuture, icdcmfuture, icdfuture, icdcnfuture, icdcntypefuture).get();
            for (string s : countmap.keyset()) {
                count += countmap.get(s);
            }
            log.info("值域更新值域完成成! 耗时 {} 秒!", (system.currenttimemillis() - now) / 1000);
            log.info("本次更新值域数量:{}条", count);

        } catch (exception e) {
            redislock.unlock(lockkey, lockvalue);
            log.error("值域更新失败{}", e.getmessage());
            throw new baseexception(e.getmessage());
        } finally {
            redislock.unlock(lockkey, lockvalue);
        }
    }

2、个人示例2

    /**
     * 异步更新值域
     */
    @override
    @async("asyncpooltaskexecutor")
    public void updaterange() {
        list<mdrange> rangelist = new arraylist<>(16);
        // 先查询rp_md_range 表中最新的时间
        final list<string> maxtime = basemapper.selectmaxtime();
        date lastupdatetime = new date();
        boolean effectivetime = collectionutil.isnotempty(maxtime) && strutil.isnotblank(maxtime.get(0));
        if (effectivetime) {
            lastupdatetime = datetimeutil.formattime(maxtime.get(0));
        }

        try {
            // 字典类型
            date finallastupdatetime = lastupdatetime;
            final list<dictdata> list = dictdataservice.list(new lambdaquerywrapper<dictdata>()
                    .eq(dictdata::getstatus, 0)
                    .gt(effectivetime, dictdata::getcreatetime, lastupdatetime)
                    .isnull(dictdata::getupdatetime)
                    .or(i -> i.eq(dictdata::getstatus, 0).gt(effectivetime, dictdata::getupdatetime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(dictdata::getdicttype, dictdata::getdictsort))
            );
            for (dictdata dictdata : list) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(dictdata.getdictvalue());
                mdrange.setvalue(dictdata.getdictlabel());
                mdrange.settype(dictdata.getdicttype());
                mdrange.setsort(sortenum.sort1.getkey());
                rangelist.add(mdrange);
            }
            // 药品
            final list<mdmedicinedata> medicinedatalist = medicinedataservice.list(new lambdaquerywrapper<mdmedicinedata>()
                    .eq(mdmedicinedata::getstatus, "1")
                    .gt(effectivetime, mdmedicinedata::getcreatetime, lastupdatetime)
                    .isnull(mdmedicinedata::getopertime)
                    .or(i -> i.eq(mdmedicinedata::getstatus, "1").gt(effectivetime, mdmedicinedata::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(mdmedicinedata::getmedicinename, mdmedicinedata::getcreatetime))
            );
            for (mdmedicinedata medicinedata : medicinedatalist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(medicinedata.getmedicinedataid());
                mdrange.setvalue(medicinedata.getmedicinename());
                mdrange.settype(sortenum.sort2.getdefaulttype());
                mdrange.setsort(sortenum.sort2.getkey());
                rangelist.add(mdrange);
            }
            // 医疗服务
            final list<server> serverlist = serverservice.list(new lambdaquerywrapper<server>()
                    .eq(server::getstatus, "1")
                    .gt(effectivetime, server::getcreatetime, lastupdatetime)
                    .isnull(server::getopertime)
                    .or(i -> i.eq(server::getstatus, "1").gt(effectivetime, server::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(server::getcategoryid, server::getcreatetime))
            );
            for (server server : serverlist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(server.getservercode());
                mdrange.setvalue(server.getservername());
                mdrange.settype(sortenum.sort3.getdefaulttype());
                mdrange.setsort(sortenum.sort3.getkey());
                rangelist.add(mdrange);
            }
            // 手术
            final list<icdcm> icdcmlist = icdcmservice.list(new lambdaquerywrapper<icdcm>()
                    .eq(icdcm::getstatus, "1")
                    .gt(effectivetime, icdcm::getcreatetime, lastupdatetime)
                    .isnull(icdcm::getopertime)
                    .or(i -> i.eq(icdcm::getstatus, "1").gt(effectivetime, icdcm::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(icdcm::getdatatype, icdcm::getcreatetime))
            );
            for (icdcm icdcm : icdcmlist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(icdcm.getcmcode());
                mdrange.setvalue(icdcm.getcmname());
                mdrange.settype(sortenum.sort5.getdefaulttype());
                mdrange.setsort(sortenum.sort5.getkey());
                rangelist.add(mdrange);
            }
            // 疾病
            final list<icd> icdlist = icdservice.list(new lambdaquerywrapper<icd>()
                    .eq(icd::getstatus, "1")
                    .gt(effectivetime, icd::getcreatetime, lastupdatetime)
                    .isnull(icd::getopertime)
                    .or(i -> i.eq(icd::getstatus, "1").gt(effectivetime, icd::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(icd::getdatatype, icd::getcreatetime))
            );
            for (icd icd : icdlist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(icd.geticdcode());
                mdrange.setvalue(icd.geticdname());
                mdrange.settype(sortenum.sort4.getdefaulttype());
                mdrange.setsort(sortenum.sort4.getkey());
                rangelist.add(mdrange);
            }
            // 中医疾病
            final list<icdcn> icdcnlist = icdcnservice.list(new lambdaquerywrapper<icdcn>()
                    .eq(icdcn::getstatus, "1")
                    .gt(effectivetime, icdcn::getcreatetime, lastupdatetime)
                    .isnull(icdcn::getopertime)
                    .or(i -> i.eq(icdcn::getstatus, "1").gt(effectivetime, icdcn::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(icdcn::getdatatype, icdcn::getcreatetime))
            );
            for (icdcn icdcn : icdcnlist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(icdcn.geticdcnid());
                mdrange.setvalue(icdcn.geticdcnname());
                mdrange.settype(sortenum.sort6.getdefaulttype());
                mdrange.setsort(sortenum.sort6.getkey());
                rangelist.add(mdrange);
            }

            // 中医症候
            final list<icdcntype> icdcntypelist = icdcntypeservice.list(new lambdaquerywrapper<icdcntype>()
                    .eq(icdcntype::getstatus, "1")
                    .gt(effectivetime, icdcntype::getcreatetime, lastupdatetime)
                    .isnull(icdcntype::getopertime)
                    .or(i -> i.eq(icdcntype::getstatus, "1").gt(effectivetime, icdcntype::getopertime, finallastupdatetime))
                    .orderbyasc(true, arrays.aslist(icdcntype::getdatatype, icdcntype::getcreatetime))
            );
            for (icdcntype icdcntype : icdcntypelist) {
                mdrange mdrange = new mdrange();
                mdrange.setkey(icdcntype.geticdcnid());
                mdrange.setvalue(icdcntype.geticdcnname());
                mdrange.settype(sortenum.sort6.getdefaulttype());
                mdrange.setsort(sortenum.sort6.getkey());
                rangelist.add(mdrange);
            }

        } catch (exception e) {
            log.info("值域更新失败{}", e.getmessage());
            throw new baseexception(e.getmessage());
        }
        // 保存
        if (rangelist.size() > 0) {
            final boolean savebatch = savebatch(rangelist, rangelist.size());
            if (savebatch) {
                log.info("本次更新值域数量:{}条", rangelist.size());
            }
        } else {
            log.info("本次无值域更新");
        }
    }

3、个人示例3(不使用@async注解)

// 自定义线程池
   @autowired
    @qualifier("asyncpooltaskexecutor")
    private threadpooltaskexecutor taskexecutor;


// 1、
 final list<callable<list<dsstsettledailypagevo>>> tasklist = new arraylist<>(16);
// 2、
 tasklist.add(() -> checkprocess(groupidstoremap, currentcheckdate, ledgervolist));
// 3、
   list<future<list<dsstsettledailypagevo>>> futures = new arraylist<>(tasklist.size());
        for (callable<list<dsstsettledailypagevo>> callable : tasklist) {
            futures.add(taskexecutor.submit(callable));
        }
        for (future<list<dsstsettledailypagevo>> future : futures) {
            dailypagevolist.addall(future.get());
        }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com