关于并发数据结构的想法

时间:2014-08-12 02:38:26

标签: java multithreading concurrency spring-batch

我不确定我能否以最清晰的方式提出问题,但我会尽我所能。

让我们说我正在从第三方api中检索一些信息。检索到的信息将是巨大的。为了获得性能提升,而不是一次性检索所有信息,我将以分页的方式检索信息(api给了我那个设施,基本上是一个迭代器)。返回类型基本上是一个对象列表。

我的目标是处理我手头的信息(包括在db和许多其他操作中进行比较和存储),同时我获得请求的分页响应。

我对专家社区的问题是,在这种情况下,您更喜欢哪种数据结构。像弹簧批处理这样的框架也可以帮助您在这种情况下获得性能提升。

我知道这个问题有点模糊,但我正在寻找一般的想法,提示和指示。

2 个答案:

答案 0 :(得分:0)

就实际并行性而言,Java中一个非常有用的构造是ThreadPoolExecutor。这可能是什么样子的粗略草图:

public class YourApp {
    class Processor implements Runnable {
        Widget toProcess;

        public Processor(Widget toProcess) {
            this.toProcess = toProcess;
        }

        public void run() {
            // commit the Widget to the DB, etc
        }
    }

    public static void main(String[] args) {

        ThreadPoolExecutor executor = 
            new ThreadPoolExecutor(1, 10, 30, 
                                   TimeUnit.SECONDS, 
                                   new LinkedBlockingDeque());

        while(thereAreStillWidgets()) {
            ArrayList<Widget> widgets = doExpensiveDatabaseCall();
            for(Widget widget : widgets) {
                Processor procesor = new Processor(widget);
                executor.execute(processor);
            }
        }

    }

}

但正如我在评论中所说:对外部API的调用昂贵。很可能最好的策略是在一次调用中从API中提取所有Widget个对象,然后在获得它们后并行处理它们。进行更多的API调用会让您每次都从服务器向您发送数据的开销 - 您可能最好以最少的次数支付该费用。

另外,请记住,如果您正在进行数据库操作,那么您的数据库可能不允许并行写入,因此您可能会在那里放慢速度。

答案 1 :(得分:0)

在这些情况下,我的数据结构是java.util.concurrent.CompletionService。

出于示例的目的,我将假设一些额外的约束:

  • 您希望一次只能向远程服务器发出一个未完成的请求
  • 您想按顺序处理结果。

这里是:

// a class that knows how to update the DB given a page of results
class DatabaseUpdater implements Callable { ... }
// a background thread to do the work
final CompletionService<Object> exec = new ExecutorCompletionService(
   Executors.newSingleThreadExecutor());

// first call
List<Object> results = ThirdPartyAPI.getPage( ... );
// Start loading those results to DB on background thread
exec.submit(new DatabaseUpdater(results));

while( you need to ) {
  // Another call to remote service
  List<Object> results = ThirdPartyAPI.getPage( ... );
  // wait for existing work to complete
  exec.take(); 
  // send more work to background thread
  exec.submit(new DatabaseUpdater(results));
}
// wait for the last task to complete
exec.take(); 

这只是一个简单的双线程设计。第一个线程负责从远程服务获取数据,第二个线程负责写入数据库。

在获取结果时(通过exec.take()),DatabaseUpdater抛出的任何异常都将传播到主线程。

祝你好运。