java - Spring Async 正在阻塞并阻止嵌套的异步调用

标签 java spring multithreading asynchronous nonblocking

谁能告诉我有没有一种方法可以使用 Spring Framework 的 @Async 注释而不阻塞/等待结果?这是一些代码来澄清我的问题:

@Service
public class AsyncServiceA {

    @Autowired
    private AsyncServiceB asyncServiceB;

    @Async
    public CompletableFuture<String> a() {
        ThreadUtil.silentSleep(1000);
        return asyncServiceB.b();
    }
}


@Service
public class AsyncServiceB {

    @Async
    public CompletableFuture<String> b() {
        ThreadUtil.silentSleep(1000);
        return CompletableFuture.completedFuture("Yeah, I come from another thread.");
    }
}

和配置:

@SpringBootApplication
@EnableAsync
public class Application implements AsyncConfigurer {

    private static final Log LOG = LogFactory.getLog(Application.class);

    private static final int THREAD_POOL_SIZE = 1;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
            final AsyncServiceA bean = ctx.getBean(AsyncServiceA.class);
            bean.a().whenComplete(LOG::info);
        };
    }

    @Override
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor getAsyncExecutor() {
        final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(THREAD_POOL_SIZE);
        executor.setMaxPoolSize(THREAD_POOL_SIZE);
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        // omitted
    }
}

当我运行应用程序时,执行程序通过调用 AsyncServiceA.a() 并离开,但它仍然持有池中的线程等待 CompletableFuture.get()方法。由于池中只有一个线程,因此无法执行 AsyncServiceB.b()。我期望线程在执行 AsyncServiceA.a() 后返回到池中,然后可用于执行 AsyncServiceB.b()

有办法吗?

注意 1:我也尝试过 ListenableFuture,但结果是一样的。

注意 2:我已经成功地手动完成了(没有@Async),方法是像这样给每个方法执行器:

异步服务A

public CompletableFuture<String> manualA(Executor executor) {
    return CompletableFuture.runAsync(() -> {
        LOG.info("manualA() working...");
        ThreadUtil.silentSleep(1000);
    }, executor)
            .thenCompose(x -> asyncServiceB.manualB(executor));
}

异步服务B

public CompletableFuture<String> manualB(Executor executor) {
    return CompletableFuture.runAsync(() -> {
        LOG.info("manualB() working...");
        ThreadUtil.silentSleep(1000);
    }, executor)
            .thenCompose(x -> CompletableFuture
                    .supplyAsync(() -> "Yeah, I come from another thread.", executor));
}

如果有人想知道,这里是 ThreadUtil

public class ThreadUtil {

    public static void silentSleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

更新:添加了非阻塞异步注释的问题 https://jira.spring.io/browse/SPR-15401

最佳答案

自 Spring 3.0 以来,@Async 支持一直是 Spring 的一部分,这远早于 Java8(或 Java 7)的存在。尽管在以后的版本中添加了对 CompletableFuture 的支持,但它仍然用于方法调用的简单异步执行。 (initial implementation 反射(reflect)/显示调用)。

为了组合回调和非阻塞操作,异步支持从未被设计或打算。

对于非阻塞支持,您需要等待 Spring 5 及其响应式(Reactive)/非阻塞核心,紧接着您可以随时 submit a ticket用于异步支持中的非阻塞支持。

关于java - Spring Async 正在阻塞并阻止嵌套的异步调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43095014/

相关文章:

java - SQLJet 表插入参数

java - 调用页面大小大于 36 的 Spring Data MongoDB 存储库方法时出现 StackOverflowError

java - 从注释处理器生成 Spring 组件

c++ - 无需等待的并发代码

java - 尽管在 java 中调整了窗体的大小,如何使 jLabels 保持附着在窗口窗体的角上?

java - 泛型。创建 2 个对象并将它们添加到另一个对象

java - 如何将 JSON 转换为简单的二维数组?

java - 为什么 Spring 的 @Transactional 不能处理 protected 方法?

java - java中优化的线程安全列表

java - 在 Java 中并行化网络绑定(bind) for 循环