同时读取多个文件

时间:2013-08-25 18:14:18

标签: java multithreading io

我正在处理我的Web服务器访问日志并将处理过的信息存储到我的数据库中。以前,我做过单线程进程。完成这个过程需要很长时间。我决定使用并发文件读取来节省执行时间。我使用Executors线程池实现了这一点。这是我的java代码。

日志文件处理程序

class FileHandler implements Runnable {

        private File file;

        public FileHandler(File file) {
            this.file = file;
        }

        @Override
        public void run() {
            try {
                byte[] readInputStream = readInputStream(new FileInputStream(file));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public static byte[] readInputStream(InputStream in) throws IOException {

            //closing the bytearrayoutput stream has no effect. @see java doc.
            ByteArrayOutputStream bos = null;
            byte[] buffer = new byte[1024];
            int bytesRead = -1;

            bytesRead = in.read(buffer);

            //no input to read.
            if(bytesRead == -1) {
                    return null;
            }

            bos = new ByteArrayOutputStream(in.available()); //creating output stream with approximate capacity.
            bos.write(buffer , 0 , bytesRead);

            try {
                while((bytesRead = in.read(buffer)) != -1) {
                    bos.write(buffer , 0 , bytesRead);
                }
            }finally {
                if(in != null) {
                    in.close();
                }
            }

            return bos.toByteArray();

         }


    }

并发文件阅读

 public class AccessLogProcessor {

        public static void main(String[] args)  {

            String[] files = {

                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"

            };

            long start = System.currentTimeMillis();

            ExecutorService executors = Executors.newFixedThreadPool(files.length);

            for(String file : files) {
                executors.execute(new FileHandler(new File(file)));
            }

            executors.shutdown();

            while(!executors.isTerminated());

            System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");

        }

}

单线程文件阅读

    public class Test {

        public static void main(String[] args) throws FileNotFoundException, IOException  {

            String[] files = {

                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"

            };

            long start = System.currentTimeMillis();

            for(String file : files) {
                FileHandler.readInputStream(new FileInputStream(file));
            }

            System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");

        }

}

10轮执行的测试结果

单线程执行:9ms。

并发执行:14毫秒。

我正在同时读取文件,但为什么timetaken比单线程执行更大?请纠正我如果我做错了什么?

2 个答案:

答案 0 :(得分:1)

我可以看到几个问题:

  1. 您的测试文件似乎非常小,并且将完全缓存在RAM中,因此您的基准测试不会模拟真正的问题,因为它不会考虑I / O时间。另外,因为它太小了,你所获得的任何改进都可能会被管理线程的开销所吸收。

  2. 除非你做的数据处理工作非常复杂,否则它主要是一个I / O绑定任务,而不是一个CPU绑定任务,除非你的文件都在不同的磁盘上,否则同时抢占一个磁盘对不同数据的请求不会让它更快地运行。实际上,对于传统的硬盘而不是固态存储,像这样的“并发”I / O可能要慢得多,因为驱动器头(the pointy arm thing)会疯狂地来回移动到达磁盘的不同部分。

  3. 在大多数系统上,System.currentTimeMillis()的准确度为+/- 10毫秒。因此,您的9 ms和14 ms 的基准测试结果实际上并没有告诉您任何内容。使用System.nanoTime()获得更准确的计时器。

  4. 您运行了多少次代码?显然每次运行程序只有一次。如果它少于几千次,那么你根本没有测量任何有用的东西,因为代码仍在编译中。不要心疼:每个人都犯了这个错误!动态编译语言的基准测试 该死的 。要了解如何编写有用的基准测试,请阅读:

    至少,您应该多次循环运行完整代码,以便完成,而不是毫秒。

  5. 在频率切换CPU(即现代CPU)上,CPU在空闲时正在低速运行。只有当它开始工作时才会逐渐加快速度,除非你将它设置为固定速度(最大电池或最大性能)模式,否则这可能会混淆基准测试。或者,修复问题4将解决问题5,因为使用长循环或添加预热代码将吸收CPU频率切换的影响。

  6. 简短的回答是,您目前的基准太小,无法提供有意义的结果。一旦你运行了更长的基准测试,更准确地模拟了真正的问题,你将能够知道多线程是否使它更快。

答案 1 :(得分:-1)

首先,您应该了解并行性适用于处理器执行的一系列指令。处理器执行一系列指令。虚拟处理器称为threads。您可以分配多个线程以并行执行多个指令。但是,这并不意味着多处理允许您通过网络连接更快地下载电影,从而使带宽倍增。这意味着您的并行线程将暂停,等待网络数据到达。好?我们应该对不理解这件简单事情的人做点什么。

此外,我必须补充一点,10毫秒是java中的时间测量错误。所以,你的读数是时间测量噪音。基准测试需要一段时间才能使系统升温并需要更长的时间间隔,以便定时器分辨率不会引起重大错误。