You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中循环异步执行问题:如何改为同步执行?

问题背景
  • 依赖版本:
    • spring-boot-starter-parent 3.0.0
    • 底层Spring Framework版本:<spring-framework.version>6.0.2</spring-framework.version>

该服务用于爬取网站在搜索引擎中的排名,调试时给每个网站设置了100个短语,总计200个短语,但循环会随意切换网站,最终结果数量达到380条(远超预期)。排查发现是异步执行导致的,打印的线程名称会变化,且Lombok的@Synchronized注解未起作用。

对应的代码实现:

import lombok.Synchronized;
...

@Service
@RequiredArgsConstructor
public class RequestService {

    @Synchronized
    public void scrape() {
        List<SiteEntity> sites = siteRepository.findAllByActiveTrue();
        System.out.println("Method: The Thread name is " + Thread.currentThread().getName());


        for (SiteEntity site : sites) {
            System.out.println("Site: " + site.getUrl());

            System.out.println("Outer: The Thread name is " + Thread.currentThread().getName());

            List<PhraseEntity> phrases = phraseRepository.findAllActivePhrasesBySite(site.getId());
            assertSameSite(phrases, site);

            final int CHUNK = 10;
            List<List<PhraseEntity>> phraseChunks = Lists.partition(phrases, CHUNK);

            for (List<PhraseEntity> chunk : phraseChunks) {

                System.out.println("Inner: The Thread name is " + Thread.currentThread().getName());

                System.out.println(chunk.get(0).getPhrase() + " : " + chunk.get(chunk.size() - 1).getPhrase());
                Document doc = sendRequest(chunk, 0);

                saveManager.save(doc);
            }
        }
    }
}

问题解答

1. Spring默认是异步执行的吗?

Spring默认不是异步执行的。常规Spring Bean的方法调用都是同步的,只有显式配置异步逻辑(比如使用@Async注解、自定义TaskExecutor等)才会触发异步执行。你遇到线程名称变化的情况,大概率是sendRequest或saveManager.save内部使用了异步线程,或者scrape方法被其他异步代码调用了。

2. 如何让该循环实现同步执行?

可以从以下几个方面处理:

  • 排查异步源头:检查sendRequest和saveManager.save方法,确认内部是否存在异步逻辑,若有则改为同步实现。
  • 替换Lombok的@Synchronized:Lombok的@Synchronized默认基于当前对象加锁,若RequestService是多实例(非单例),锁会失效。可以手动使用synchronized代码块,指定全局锁对象:
    @Service
    @RequiredArgsConstructor
    public class RequestService {
        private final Object globalLock = new Object();
    
        public void scrape() {
            synchronized(globalLock) {
                // 原方法完整逻辑
            }
        }
    }
    
    同时确保RequestService是单例(Spring默认单例,除非手动修改了作用域)。
  • 保证调用链路同步:如果scrape方法是被带有@Async注解的方法调用的,需要去掉调用方的异步配置,确保整个调用链路都是同步执行。

内容的提问来源于stack exchange,提问作者Kifsif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:10:27