协程中用runInterruptible调用ListenableFuture.get()是否阻塞线程?如何非阻塞?
问题解答
问题1:即使在runInterruptible中,协程内调用com.google.common.util.concurrent.ListenableFuture.get()是否会阻塞线程?
会阻塞线程。runInterruptible仅为阻塞操作提供协程中断支持——当协程被取消时,它会中断执行阻塞调用的线程,但**get()本身仍是阻塞式调用**,会占用Dispatchers.IO线程池中的一个线程,直到ListenableFuture执行完成。
问题2:使用gRPC返回的ListenableFuture并调用其get()方法,是否会阻塞执行该调用的线程?或是因为处于协程中,线程会被释放,仅在网络响应到达时才恢复?
会阻塞线程。无论是否处于协程环境,ListenableFuture.get()都是原生的阻塞方法,会占用当前执行线程直到gRPC响应返回。协程不会自动将阻塞调用转为非阻塞挂起,runInterruptible仅增加了中断能力,并未改变get()阻塞线程的本质,线程不会被释放。
是否存在非阻塞的使用方式?
存在。可以借助kotlinx-coroutines-guava库的扩展函数,将ListenableFuture转换为协程的Deferred对象,实现真正的非阻塞等待。
修改后的代码示例:
@RestController class SomeController( private val stub: YourGrpcStubType ) { @PostMapping("/create-table") suspend fun createTable(@RequestBody request: CreateCDPTableRequest): CreateTableResponse { val responseFuture: ListenableFuture<Response> = stub.someGrpcCall(requestBuilder.build()) // 将ListenableFuture转为Deferred,非阻塞等待结果 val response = responseFuture.asDeferred().await() // 转换并返回响应 return mapToCreateTableResponse(response) } }
使用asDeferred()后,await()会挂起协程而非阻塞线程,线程会被放回Dispatchers.IO线程池复用,直到gRPC响应到达后协程才恢复执行。
内容的提问来源于stack exchange,提问作者Guy Assaf
相关产品推荐
相关产品推荐

