我们的应用程序有许多实现ListenableFuture基于 API 的服务,包括:
public interface MyService {
ListenableFuture<Thing> getMyThing();
ListenableFuture<?> putMyThing(Thing thing);
}
Run Code Online (Sandbox Code Playgroud)
由于我们的域模型根本不是线程安全的,因此除了上述服务之外,我们的大部分代码都在众所周知的单线程上运行Executor。我认为如果服务能够保证添加到Future它们生成的 s 中的任何侦听器都将被调用,那就太好了Executor。
当然,我可以通过调用 或使用适当的参数在服务的客户端中很好地强制执行此操作,ListenableFuture.addListener但我的目标正是降低客户端代码中Futures.addCallback的复杂性和错误的可能性,所以我想要侦听器当调用这些方法而不传递参数时,会发生众所周知的调用。Futures.transformExecutorExecutorExecutor
所以,现在我一直以这种方式实现服务的方法:
class MyServiceImpl {
private Executor executor; /* the "main" executor */
public ListenableFuture<Thing> getMyThing() {
ListenableFuture<Thing> future = ...; /* actual service call */
return Futures.transform(future, Functions.<Thing>identity(), executor );
}
}
Run Code Online (Sandbox Code Playgroud)
首先,这有效吗?从番石榴来源来看,似乎确实如此,但我很高兴得到某种确认,并且我在考虑对此进行单元测试时遇到了一些困难。
此外,我有点担心整个“服务在指定线程上回调(默认情况下)”模式的有用性/成本比。有人有这样的经验吗?这种方法是否存在任何隐藏的陷阱?
您提出的identity()解决方案应该可行——但有两个例外。
证明:首先,请注意,了解输入何时完成的唯一合理方法Futures.transform是通过调用input.addListener(). 我们知道addListener()尊重指定的执行人。(如果您不相信我:ListenableFutureTaskuses ExecutionList,它仅在两个地方调用侦听器:add()和execute()。在这两个地方,它都使用给定的执行器。)因此,在输入Future完成时运行的任何任务都将在给定的执行器中运行。
这里的关键词是“在输入Future完成时运行”。例外情况:
Future,它将在调用 的线程中运行addListener。Future(与原始包装器相反),侦听器将在调用 的线程中运行cancel。根据您的应用程序的结构,这些异常可能没问题,但需要牢记在心。如果它们是一个问题,您可能需要使用ForwardingListenableFuture解决方案(例如 eneveu 的解决方案,但显然不太严格)。
| 归档时间: |
|
| 查看次数: |
3196 次 |
| 最近记录: |