【发布时间】:2017-03-29 13:31:55
【问题描述】:
谁能告诉我有没有一种方法可以使用 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的问题
【问题讨论】:
-
那么你对最大池大小为 1 的期望是什么...只有一个线程来处理异步方法,所以基本上没有什么是异步的。该线程被阻塞并且在完成之前不会返回到池中。
-
我已经在上面的问题中解释了我的期望 - CompletableFuture 上的操作是非阻塞的,直到您调用 get。我不会在任何地方调用 get(),所以我希望线程可以使用这个 CompletableFuture 完成并返回到池中。不幸的是,@Async 注释强制调用 get() 方法,因此线程被阻塞 - 我想摆脱它。我知道这与大小为 1 的线程池不是异步的,但在非阻塞场景中它会像魅力一样工作。
-
我不确定我是否理解您的最后评论。我从来没有达到 b() 方法调用 - a(),它是异步返回并阻塞线程 before b() 在另一个线程中被调用,并且池中没有线程用于 b( )。我确信 b() 永远不会被期望,因为我已经在输入方法和返回时添加了方面来记录。我也调试了几十次。
-
我删除了评论(在我写另一个之前:))。 Spring 正在调用
get(),因此它的工作方式与您编写内容的示例不同。所以基本上你是在比较苹果和橘子,你想要的不能这样实现,你仍然需要自己做曲。 spring 也用于supplyAsync而不是runAsync。 (您可能想查看AsyncExecutionInterceptor的来源,以了解 spring 的实际作用。 -
:) 是的,我看到了
AsyncExecutionInterceptor的工作原理,但我不喜欢它(因为我想控制何时调用get())。我希望对此有一些解决方法(使用配置或其他东西),因为这对我来说没有意义,为什么它会那样工作(阻止执行)。好吧,也许我要求太多了,应该做自定义解决方案,然后等待 Java 9 和 Spring 5.0 的正式发布,以及它的反应特性。
标签: java spring multithreading asynchronous nonblocking