在调用堆栈外部调用 OperationFuture 时 SynchronizationContext 出现异常
Exception in SynchronizationContext when calling OperationFuture outside of callstack
我正在使用 Google 的 DocumentAI SDK,但这个错误似乎源于 gRPC SDK。我在 DocumentAI 中调用异步操作,其中 returns 一个 OperationFuture
。当我在创建未来的调用堆栈框架内调用方法 OperationFuture.get()
时,代码会正确阻塞,直到未来完成并正常继续。但是,如果创建未来的方法 returns 并且我在其创建框架之外调用 OperationFuture.get()
我总是会遇到以下堆栈跟踪的异常
io.grpc.internal.ManagedChannelImpl uncaughtException
SEVERE: [Channel<1>: (us-documentai.googleapis.com:443)] Uncaught exception in the SynchronizationContext. Panic!
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@60d4b478 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@1e3a60f5[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:326)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:533)
at java.util.concurrent.ScheduledThreadPoolExecutor.execute(ScheduledThreadPoolExecutor.java:622)
at io.grpc.internal.ManagedChannelImpl$RealChannel$PendingCall.reprocess(ManagedChannelImpl.java:1089)
at io.grpc.internal.ManagedChannelImpl$RealChannel.updateConfigSelector(ManagedChannelImpl.java:1022)
at io.grpc.internal.ManagedChannelImpl$NameResolverListenerNamesResolved.run(ManagedChannelImpl.java:1729)
at io.grpc.SynchronizationContext.drain(SynchronizationContext.java:95)
at io.grpc.SynchronizationContext.execute(SynchronizationContext.java:127)
at io.grpc.internal.ManagedChannelImpl$NameResolverListener.onResult(ManagedChannelImpl.java:1815)
at io.grpc.internal.DnsNameResolver$Resolve.run(DnsNameResolver.java:333)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
示例伪代码
public class Engine {
public void startAsync() {
...
OperationFuture future = googleClient.doAsyncRequest();
// calling future.get(); here works fine
this.operationFuture = future;
...
}
}
public class Main {
public static void main(String[] args) {
...
Engine eng = new Engine();
eng.startAsync();
eng.operationFuture.get(); // this doesn't work
...
}
}
我找到问题的根源了。该操作在堆栈框架内而不在外部起作用的原因是因为创建和管理此 operationFuture
的 googleClient
对象有自己的 ExecutorService
来处理 operationFuture
生命周期。一旦我们从 startAsync()
方法中 return , googleClient
对象就会超出范围并被释放,这也会释放与其关联的所有线程并破坏 operationFuture
对象。
为了解决这个问题,googleClient
对象必须与 operationFuture
一起在内存中保持活动状态。例如 DocumentAI:
public void startAsync() {
...
this.googleClient = DocumentProcessorServiceClient.create(docAISettings);
this.operationFuture = this.googleClient.doAsyncRequest();
return;
}
只要 future 和客户端对象都没有超出范围,现在从外部调用 operationFuture.get()
方法可以正常工作。
我已经尝试为 googleClient
对象提供一个自定义线程池并让它被垃圾收集(即 googleClient
对象死亡但线程 运行 operationFuture
没有)但它似乎不起作用,不知道为什么。
我正在使用 Google 的 DocumentAI SDK,但这个错误似乎源于 gRPC SDK。我在 DocumentAI 中调用异步操作,其中 returns 一个 OperationFuture
。当我在创建未来的调用堆栈框架内调用方法 OperationFuture.get()
时,代码会正确阻塞,直到未来完成并正常继续。但是,如果创建未来的方法 returns 并且我在其创建框架之外调用 OperationFuture.get()
我总是会遇到以下堆栈跟踪的异常
io.grpc.internal.ManagedChannelImpl uncaughtException
SEVERE: [Channel<1>: (us-documentai.googleapis.com:443)] Uncaught exception in the SynchronizationContext. Panic!
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@60d4b478 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@1e3a60f5[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:326)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:533)
at java.util.concurrent.ScheduledThreadPoolExecutor.execute(ScheduledThreadPoolExecutor.java:622)
at io.grpc.internal.ManagedChannelImpl$RealChannel$PendingCall.reprocess(ManagedChannelImpl.java:1089)
at io.grpc.internal.ManagedChannelImpl$RealChannel.updateConfigSelector(ManagedChannelImpl.java:1022)
at io.grpc.internal.ManagedChannelImpl$NameResolverListenerNamesResolved.run(ManagedChannelImpl.java:1729)
at io.grpc.SynchronizationContext.drain(SynchronizationContext.java:95)
at io.grpc.SynchronizationContext.execute(SynchronizationContext.java:127)
at io.grpc.internal.ManagedChannelImpl$NameResolverListener.onResult(ManagedChannelImpl.java:1815)
at io.grpc.internal.DnsNameResolver$Resolve.run(DnsNameResolver.java:333)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
示例伪代码
public class Engine {
public void startAsync() {
...
OperationFuture future = googleClient.doAsyncRequest();
// calling future.get(); here works fine
this.operationFuture = future;
...
}
}
public class Main {
public static void main(String[] args) {
...
Engine eng = new Engine();
eng.startAsync();
eng.operationFuture.get(); // this doesn't work
...
}
}
我找到问题的根源了。该操作在堆栈框架内而不在外部起作用的原因是因为创建和管理此 operationFuture
的 googleClient
对象有自己的 ExecutorService
来处理 operationFuture
生命周期。一旦我们从 startAsync()
方法中 return , googleClient
对象就会超出范围并被释放,这也会释放与其关联的所有线程并破坏 operationFuture
对象。
为了解决这个问题,googleClient
对象必须与 operationFuture
一起在内存中保持活动状态。例如 DocumentAI:
public void startAsync() {
...
this.googleClient = DocumentProcessorServiceClient.create(docAISettings);
this.operationFuture = this.googleClient.doAsyncRequest();
return;
}
只要 future 和客户端对象都没有超出范围,现在从外部调用 operationFuture.get()
方法可以正常工作。
我已经尝试为 googleClient
对象提供一个自定义线程池并让它被垃圾收集(即 googleClient
对象死亡但线程 运行 operationFuture
没有)但它似乎不起作用,不知道为什么。