从执行异步任务的java方法返回值

时间:2014-04-09 21:27:25

标签: java multithreading asynchronous multiprocessing

目前我有一个Web界面,用户输入一些数据,如start_date和end_date,然后单击一个按钮,该按钮向servlet控制器发出Ajax请求,该控制器执行一个方法,该方法接受用户输入的数据并执行如下操作:

在表示具有相同结构的数据库表的值列表的for循环中,有一个查询,并且使用结果集进行了额外的计算,即

for(int i=0; i<list.size(); i++)
{
    String dbTable = "vehicle_" + list.get(i).getTableName(); 

    Object afterComputationValue = someMethodThatQueriesDataAndReturnValue(dbTable);
} 

然后将对象 afterComputationValue 放入列表中,此列表需要返回到servlet,然后序列化并返回给用户。

因此返回列表的方法如下所示:

private List<Object> theMethod(String startDate, String endDate)
{
     List<Object> theList = new ArrayList<>();

     List<AnotherObject> databaseData = SomeClass.getVehiclesList();
     for(int i=0; i<list.size(); i++)
     {
        String dbTable = "vehicle_" + list.get(i).getTableName(); 
        Object afterComputationValue = someMethodThatQueriesDataAndReturnValue(dbTable);
        theList.put(afterComputationValue);
     }

     return theList; 
}

不幸的是,执行此方法需要花费一分多钟时间,所以我想如果我获得8核心服务器的优势,如果制作方法,它可能会快8倍,可以在单独的线程中计算所有查询

所以,如果我对此我是对的,我想问一下如何让这个方法在所有线程(它们的大小取决于列表大小)完成任务后返回值?

4 个答案:

答案 0 :(得分:2)

您可以使用ExecutorService。代码可以是这样的(代码未经过测试):

private List<Object> theMethod(String startDate, String endDate) {
    final List<Object> theList = Collections.synchronizedList(new ArrayList<>());
    final List<AnotherObject> databaseData = SomeClass.getVehiclesList();
    ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
    for(int i=0; i<list.size(); i++) {
        final index = i;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                String dbTable = "vehicle_" + list.get(index).getTableName(); 
                ResultSet rs = someMethodThatReturnsResultSet(dbTable);
                Object afterComputationValue = anotherMethodToDoSomeComputation(rs); 
                theList.put(afterComputationValue);
            }
        })
    }
    executor.shutdown();
    try {
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    } 
    catch (InterruptedException e) {
        // Do something
    }
    return theList; 
}

来自评论:

请注意,如果一个或多个线程失败,此方法仍会返回列表。如果这不是预期的行为,您必须1)抓住Exception方法中的run()并正确对待或2)检查theList是否与原始{{1}的大小相同(如果尺寸不同,出错了)。

答案 1 :(得分:1)

从给定的代码中,我们不确定瓶颈是someMethodThatReturnsResultSet还是某些其他方法,例如anotherMethodToDoSomeComputation

假设someMethodThatReturnsResultSet是瓶颈,那么可能你必须调整使用的查询或通过单个数据库连接进行数据库调用,关闭资源,使用JNDI连接数据库而不是天真{{1}等等。

假设Class.forName(...); DriverManager.getConnection(...)anotherMethodToDoSomeComputation或其中一个是瓶颈,那么请提供代码以分析其性能是否可以使用并发性进行改进。

要确定哪种方法是瓶颈,不要相信您当前的知识(除非您有丰富的经验,并且我的意思是拥有多年的经验并且可能是白发),请使用像Visual VM这样的分析器或Java Mission Control。


来自你的评论:

  

对数据库的查询很慢!

然后你必须调整它:

  • 更改SomeClass.getVehiclesList()子句
  • 中字段的顺序
  • 检查是否可以向与查询相关的数据库添加更多索引
  • 向数据库服务器添加更多RAM
  • 检查数据库引擎上的特定调整选项。

请注意,在数据库查询上使用并发不会加速该过程,因为毕竟是磁盘读取操作,默认情况下很慢。此外,如果多个请求位于同一个表中,则数据库引擎将同步多个请求。

答案 2 :(得分:1)

拥有CountDownLatch allResultsDone = new CountDownLatch(list.size())

for循环中,创建代表每个任务的一个表处理的Runnable个任务,并在某些ExecutorService上提交它们。在每个Runnable中,执行allResultsDone.countDown()表示此任务已完成。

在负责收集所有结果并返回客户端的方法中,在返回之前执行allResultsDone.await()。当所有任务完成后,此锁存器将自动跳闸,方法线程可以返回。

答案 3 :(得分:1)

您有两个不错的选择:

  1. 您可以使用fork / join框架    有关详细信息,请查看此链接,但您可以找到大量示例代码。       http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html

  2. 您可以使用CompletionService:我在这里添加一些示例代码,但这只是示例代码,您需要根据需要进行调整。

    public class CompletionServiceExample {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            calculate();
        }
    
        private static List<Object> calculate() throws InterruptedException, ExecutionException {
            List<Object> theList = new ArrayList<>();
    
            final List<AnotherObject> vehiclesList = SomeClass.getVehiclesList();
            ExecutorService taskExecutor = Executors.newFixedThreadPool(vehiclesList.size());
            java.util.concurrent.CompletionService<ResultSet> rsCompletionService = new ExecutorCompletionService<>(taskExecutor);
            java.util.concurrent.CompletionService<Object> completionServiceForAnotherCalc = new ExecutorCompletionService<>(taskExecutor);
            for(int i=0; i< vehiclesList.size(); i++)
            {
                final String dbTable = "vehicle_" + vehiclesList.get(i).getTableName();
                rsCompletionService.submit(new Callable<ResultSet>() {
                    @Override
                    public ResultSet call() throws Exception {
                        return someMethodThatReturnsResultSet(dbTable);
                    }
                }) ;
    
                for(int v=0; v < vehiclesList.size(); v++){
                    final Future<ResultSet> vehicleRs = rsCompletionService.take();
                    completionServiceForAnotherCalc.submit(new Callable<Object>() {
                        @Override
                        public Object call() throws Exception {
                            return anotherMethodToDoSomeComputation((ResultSet) vehicleRs);
                        }
                    });
                }
    
                for(int v=0; v < vehiclesList.size(); v++){
                    Future<Object> finalData = completionServiceForAnotherCalc.take();
                    theList.add(finalData.get());    
                }
            }
    
            return theList;
        }
    
        private static Object anotherMethodToDoSomeComputation(ResultSet rs) {
            return null; 
        }
    
        private static ResultSet someMethodThatReturnsResultSet(String dbTable) {
            return null; 
        }
    }
    
  3. 希望这会有所帮助。如果您需要进一步澄清,请告诉我。