Java 在回调中使用 Spring 4.0 的新 ListenableFuture - 奇怪的结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23681822/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:40:02  来源:igfitidea点击:

Using Spring 4.0's new ListenableFuture With Callbacks - Odd Results

javaspringthreadpooljava.util.concurrent

提问by Bal

I have a web app that takes an array of IDs, queries an external webservice for each ID one at a time and publish each result as it arrives to a WebSocket client via a STOMP broker. I can get this to work using simple Futures, but I'm trying to use Spring 4's new ListenableFutures and provide a callback.

我有一个 Web 应用程序,它采用一组 ID,一次为每个 ID 查询一个外部 Web 服务,并在每个结果通过 STOMP 代理到达 WebSocket 客户端时发布它。我可以使用简单的 Futures 让它工作,但我正在尝试使用 Spring 4 的新 ListenableFutures 并提供回调。

The working code uses a ThreadPoolTaskExecutor that is defined in my root config. I have a class called "SosQuery" with a method called "test" that is annotated with @Async and returns an AsyncResult. Here is my working code being called from a root context service class:

工作代码使用在我的根配置中定义的 ThreadPoolTask​​Executor。我有一个名为“SosQuery”的类,其中包含一个名为“test”的方法,该方法使用 @Async 进行注释并返回一个 AsyncResult。这是从根上下文服务类调用的我的工作代码:

@Override
    public void test(String[] oids) throws Exception {
        List<Future<String>> futures = new ArrayList<Future<String>>();

        for (String oid : oids) {
            futures.add(sosQuery.test(oid));
        }

        while (!futures.isEmpty()) {
            List<Future<String>> done = new ArrayList<Future<String>>();
            for (Future<String> future : futures) {
                if (future.isDone()) {
                    messagingTemplate.convertAndSendToUser("me", "/queue/observation", future.get());
                    done.add(future);
                }
            }
            futures.removeAll(done);
        }
    }

This works fine and I see the responses arriving in my client. I modified the SosQuery method that is defined with the @Async annotation to simply return "String", and created a SimpleAsyncTaskExecutor in my root config. Here is the modified method to use ListenableFuture:

这工作正常,我看到响应到达我的客户端。我修改了用@Async 注释定义的 SosQuery 方法以简单地返回“String”,并在我的根配置中创建了一个 SimpleAsyncTaskExecutor。这是使用 ListenableFuture 的修改方法:

 @Override
    public void test(String[] oids) throws Exception {
        for (final String oid : oids) {
              ListenableFuture<String> task = asyncTaskExecutor.submitListenable(new Callable<String>(){
                @Override
                public String call() throws Exception {
                    String result = sosQuery.test(oid);
                    logger.debug("result for sosQuery: " + result);
                    return result;
                }
            });

            task.addCallback(new ListenableFutureCallback<String>() {

                @Override
                public void onSuccess(String result){
                    if (result == null){
                        result = "ITS NULL";
                    }
                    messagingTemplate.convertAndSendToUser("me", "/queue/observation", result);
                }

                @Override
                public void onFailure(Throwable t){
                    logger.error("Error executing callback.", t);
                }
            });
        }
    }

I'm seeing weird behavior... when I deploy in debug mode, I can see that the call() method is being executed and the result is being built from the SosQuery class properly, however my logger statement never appears in the logs. Immediately aftewards, the onSuccess method executes, but the result String is null.

我看到了奇怪的行为......当我在调试模式下部署时,我可以看到正在执行 call() 方法并且结果是从 SosQuery 类正确构建的,但是我的记录器语句从未出现在日志中。之后,onSuccess 方法立即执行,但结果字符串为空。

The onFailure method never gets called and there is absolutely nothing distinctive in the logs. Documentation for using the ListableFutures is scarce and tightly coupled to the AsyncRestTemplate, but little exists for just creating your own tasks. Does anybody have any idea what I might be doing wrong?

onFailure 方法永远不会被调用,并且日志中绝对没有什么特别之处。使用 ListableFutures 的文档很少,并且与 AsyncRestTemplate 紧密耦合,但仅用于创建自己的任务的文档很少。有人知道我可能做错了什么吗?

采纳答案by Mani

You should remove @Async in your SosQuery.test method.

您应该在 SosQuery.test 方法中删除 @Async。

ListenableFuture<String> task = asyncTaskExecutor.submitListenable(new Callable<String>(){
                @Override
                public String call() throws Exception {
                    String result = sosQuery.test(oid);
                    logger.debug("result for sosQuery: " + result);
                    return result;
                }
            });

Here the content inside the call() method is already invoked in separate thread . if you have @Async in test method. then it would create another thread and return immediately( that why you are getting response immediately before the test method completes)

这里 call() 方法中的内容已经在单独的 thread 中调用了。如果您在测试方法中有@Async。然后它会创建另一个线程并立即返回(这就是为什么你在测试方法完成之前立即得到响应)

And another important note from Doc**

来自Doc** 的另一个重要说明

    This implementation does not reuse threads! Consider a thread-pooling TaskExecutor 
implementation instead, in particular for executing a large number of short-lived tasks.