我正在运行一个程序,我下载大文件,解析它们,然后将我从文件中提取的数据写入另一个文件。
文件需要很长时间才能下载和解析,但写入任务平均只需要一分钟左右。我把它放在一起的解决方案是有三个三线程的fixedthreadpool。
ExecutorService downloadExecutor = Executors.newFixedThreadPool(3);
ExecutorService parseExecutor = Executors.newFixedThreadPool(3);
ExecutorService writeExecutor = Executors.newFixedThreadPool(3);
下载池中的线程下载文件,然后将新线程提交到解析器线程池,文件名作为参数。这是在线程本身内完成的。然后,下载线程将从文件URL列表中下载另一个文件。
解析器线程完成从文件中解析出我想要的数据后,它会将包含数据的新线程提交给写入线程池,然后将其写入.csv文件。
我的问题是,如果有一个更优雅的解决方案。我没有真正做过复杂的线程。由于我有很多文件要下载和解析,我不希望任何线程在任何时候都处于空闲状态。再次提出的想法是,由于解析文件可能需要一段时间,因此我不妨制作单独的线程来专门下载这些文件。
答案 0 :(得分:8)
为什么不只使用一个线程池。下载,解析和保存必须相互等待,所以最好的任务分离是每个文件使用一个线程。
答案 1 :(得分:2)
这并不是一个糟糕的做法,因为许多开发人员都在进行类似的编码。但是你需要记住一些事情。
第一,您不能仅仅因为您拥有更多线程而期望性能提升。根据CPU的数量,有最佳线程数。
第二,你必须确保如何处理异常。
第三,您必须确保在需要停止应用程序的事件中关闭所有线程池。
答案 2 :(得分:2)
所以你的问题有两个方面:
读取和写入文件是IO绑定的。 Async IO是IO绑定任务的最佳选择。 Java有AsynchronousFileChannel,允许您读取和写入文件,而不必担心通过完成处理程序实现延续的线程池。 Complete Example.
AsynchronousFileChannel ch = AsynchronousFileChannel.open(path);
final ByteBuffer buf = ByteBuffer.allocate(1024);
ch.read(buf, 0, 0,
new CompletionHandler() {
public void completed(Integer result, Integer length){
..
}
public void failed(Throwable exc, Integer length) {
..
}
}
);
你为写作做同样的事情,你只需要写入频道
ch.write(...
对于解析文件,这是一个计算绑定任务,并且你应该让你的CPU核心热,你可以分配一个等于你拥有的核心数的线程池。
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
现在需要记住的是:您需要测试代码,并且测试并发代码很难。如果您无法证明其正确性,请不要这样做。