Java ListenableFuture回调执行顺序
Guava的Java ListenableFuture回调执行顺序,java,parallel-processing,java-7,guava,Java,Parallel Processing,Java 7,Guava,Guava的ListenableFuture库提供了一种向未来任务添加回调的机制。具体做法如下: ListenableFuture<MyClass> future = myExecutor.submit(myCallable); Futures.addCallback(future, new FutureCallback<MyClass>() { @Override public void onSuccess(@Nullable MyClass myCla
ListenableFuture
库提供了一种向未来任务添加回调的机制。具体做法如下:
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
doSomething(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
final Builder b;
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
b.setMyClass(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
// Do the same thing for all other fields.
我的问题是,是否保证在get
终止之前运行某个未来的所有回调。也就是说,如果将来注册了多个回调执行器上的多个回调,那么所有回调是否会在get
返回之前完成
编辑
我的用例是,我将构建器传递给许多类。每个类填充生成器的一个字段。我希望异步填充所有字段,因为每个字段都需要一个外部查询来生成字段的数据。我希望调用我的asyncPopulateBuilder
的用户收到一个Future
,她可以在上面调用get
,并确保所有字段都已填充。我的想法如下:
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
doSomething(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
final Builder b;
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
b.setMyClass(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
// Do the same thing for all other fields.
final Builder b;
ListenableFuture=myExecutor.submit(myCallable);
Futures.addCallback(future,newfuturecallback(){
@凌驾
成功时公共无效(@Nullable MyClass MyClass){
b、 设置myClass(myClass);
}
@凌驾
失效时的公共无效(可丢弃的t){
打印警告(t);
}},myCallbackExecutor);
}
//对所有其他字段执行相同的操作。
在这种情况下,在填充所有字段之前,建议使用什么方法进行阻止?在
get
返回之前,不保证运行回调。下面将详细介绍
至于如何解决这个用例,我建议将每个字段数据的查询转换为单独的未来
,将它们与allAsList
+transform
相结合,并对此采取行动。(我们可能有一天会提供。)
ListenableFuture=myExecutor.submit(myCallable);
最终名单未来富=
期货市场转型(
未来,,
新函数(){…},
myCallbackExecutor);
最终列出的未来条形图=。。。;
最终可上市未来baz=。。。;
ListenableFuture allAvailable=Futures.allAsList(foo、bar、baz);
ListenableFuture allSet=Futures.transform(
所有可用的,
新函数(){
@凌驾
应用公共对象(忽略对象){
//使用getUnchecked,因为我们知道他们已经成功了:
builder.setFoo(Futures.getUnchecked(foo));
builder.setFoo(Futures.getUnchecked(bar));
builder.setFoo(Futures.getUnchecked(baz));
返回null;
}
}
};
现在用户可以调用allSet.get()
等待填充
(或者您希望allSet
成为Future
,以便将引用传递给构建器。或者您根本不需要Future
的完整版本,只需要countdownlock
,在该版本中,您可以使用addCallback
而不是transform
,并在最后对闩锁进行倒计时。)回调。)
这种方法还可以简化错误处理
回复:“回调是否在
get
之前运行?”
首先,我非常确定,我们在规范中的任何地方都不能保证这一点,因此感谢您的提问,而不仅仅是为了:)如果您最终希望依赖当前实现的某些行为,请让我们添加文档和测试
第二,如果我从字面上理解你的问题,你所要求的是不可能的:如果get()
等待所有侦听器完成,那么任何调用get()
的侦听器都将挂起
您的问题的一个稍微宽松的版本是“所有侦听器是否至少在get()
返回之前启动?”这也是不可能的:假设我将两个侦听器附加到同一Future
上,以便与directExecutor()
一起运行。两个侦听器只需调用get()
并返回。其中一个侦听器必须首先运行。当它调用get()
时,它将挂起,因为第二个侦听器还没有启动——在第一个侦听器完成之前,它也不能启动。(更一般地说,依赖任何给定的执行器来迅速执行任务可能是危险的。)
更宽松的版本是“在get()
返回之前,Future
是否至少为每个侦听器调用submit()
”,但这最终会在与我刚才描述的相同的场景中出现问题:在directExecutor()上调用submit(firstListener)
运行任务并调用get()
,在启动第二个侦听器之前无法完成,在第一个侦听器完成之前无法完成
如果说有什么区别的话,那就是在任何侦听器执行之前,get()
将返回的可能性似乎要大得多。但是由于线程调度的不可预测性,我们也不能依赖它。(再说一次:它没有文档化,所以请不要依赖它,除非您要求对它进行文档化!)final Builder b;
CountDownLatch闩锁=新的CountDownLatch(1);
ListenableFuture=myExecutor.submit(myCallable);
Futures.addCallback(future,newfuturecallback(){
@凌驾
成功时公共无效(@Nullable MyClass MyClass){
b、 设置myClass(myClass);
倒计时();
}
@凌驾
失效时的公共无效(可丢弃的t){
打印警告(t);
倒计时();
},myCallbackExecutor);
试一试{
satch.wait();
}捕捉(中断异常e){
LOG.error(“某物中断异常”,e);
}最后{
myCallbackExecutor.shutdown();
}
编辑
代码的灵感来自@Chris Povirk
(或者您希望allSet成为未来,这样用户就可以获得对构建器的引用。或者您根本不需要一个完整的未来,只需要一个倒计时闩锁,您可以使用addCallback而不是transform,并在回调结束时倒计时闩锁。)
这种方法还可以简化错误处理
谢谢你的回答!
final Builder b;
CountDownLatch latch = new CountDownLatch(1);
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
b.setMyClass(myClass);
latch.countDown();
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
latch.countDown();
}, myCallbackExecutor);
try {
latch.await();
} catch (InterruptedException e) {
LOG.error("something InterruptedException", e);
} finally {
myCallbackExecutor.shutdown();
}