使用多线程读取单个文件:是否能提高速度?

7

我正在读取一个包含500000行的文件。 我正在测试多线程如何加速这个过程....

private void multiThreadRead(int num){

    for(int i=1; i<= num; i++) { 
        new Thread(readIndivColumn(i),""+i).start(); 
     } 
}

private Runnable readIndivColumn(final int colNum){
    return new Runnable(){
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {

                long startTime = System.currentTimeMillis();
                System.out.println("From Thread no:"+colNum+" Start time:"+startTime);

                RandomAccessFile raf = new RandomAccessFile("./src/test/test1.csv","r");
                String line = "";
                //System.out.println("From Thread no:"+colNum);

                while((line = raf.readLine()) != null){
                    //System.out.println(line);
                    //System.out.println(StatUtils.getCellValue(line, colNum));
                }


                long elapsedTime = System.currentTimeMillis() - startTime;

                String formattedTime = String.format("%d min, %d sec",  
                        TimeUnit.MILLISECONDS.toMinutes(elapsedTime), 
                        TimeUnit.MILLISECONDS.toSeconds(elapsedTime) -  
                        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(elapsedTime)) 
                    );

                System.out.println("From Thread no:"+colNum+" Finished Time:"+formattedTime);
            } 
            catch (Exception e) {
                // TODO Auto-generated catch block
                System.out.println("From Thread no:"+colNum +"===>"+e.getMessage());

                e.printStackTrace();
            }
        }
    };
}

private void sequentialRead(int num){
    try{
        long startTime = System.currentTimeMillis();
        System.out.println("Start time:"+startTime);

        for(int i =0; i < num; i++){
            RandomAccessFile raf = new RandomAccessFile("./src/test/test1.csv","r");
            String line = "";

            while((line = raf.readLine()) != null){
                //System.out.println(line);
            }               
        }

        long elapsedTime = System.currentTimeMillis() - startTime;

        String formattedTime = String.format("%d min, %d sec",  
                TimeUnit.MILLISECONDS.toMinutes(elapsedTime), 
                TimeUnit.MILLISECONDS.toSeconds(elapsedTime) -  
                TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(elapsedTime)) 
            );

        System.out.println("Finished Time:"+formattedTime);
    }
    catch (Exception e) {
        e.printStackTrace();
        // TODO: handle exception
    }

}
    public TesterClass() {

    sequentialRead(1);      
    this.multiThreadRead(1);

}

当num=1时,我得到了以下结果:

开始时间:1326224619049

完成时间:2分14秒

顺序读取结束...........

多线程读取开始:

从线程号1开始时间为:1326224753606

从线程号1完成时间为:2分13秒

多线程读取结束.....

当num=5时,我得到了以下结果:

    formatted Time:10 min, 20 sec

Sequential read ENDS...........

Multi-Thread read starts:

From Thread no:1 Start time:1326223509574
From Thread no:3 Start time:1326223509574
From Thread no:4 Start time:1326223509574
From Thread no:5 Start time:1326223509574
From Thread no:2 Start time:1326223509574
From Thread no:4 formatted Time:5 min, 54 sec
From Thread no:2 formatted Time:6 min, 0 sec
From Thread no:3 formatted Time:6 min, 7 sec
From Thread no:5 formatted Time:6 min, 23 sec
From Thread no:1 formatted Time:6 min, 23 sec
Multi-Thread read ENDS.....

我的问题是:多线程读取不应该只需要大约2.13秒吗?您能否解释为什么使用多线程解决方案需要太长时间?

提前感谢。


除非它们写入不同的磁盘,否则线程将无法工作,在这种情况下,两个线程都争夺写入同一个文件。因此,在这种情况下,线程将无法工作。 - user982733
@TomaszNurkiewicz - 不一样,那个是每个文件使用一个线程。 - Kelly S. French
3个回答

10

你在并行读取时看到速度变慢的原因是因为磁盘头需要搜索下一个读取位置(大约需要5毫秒)从而导致每个线程都需要等待。因此,使用多个线程进行读取会有效地使磁盘在寻道之间来回反弹,从而减慢读取速度。建议使用单个线程顺序读取文件,这是从单个磁盘中读取文件的唯一推荐方法。


感谢您的评论。尽管多线程读取需要更长时间(例如,6.2分钟而不是2.10分钟),但是当我使用顺序读取迭代5次时,仍然可以节省4/5分钟(即顺序读取= 10.20分钟;而5个线程= 6.20分钟)。 - Hasan

9

由于文件读取主要是等待磁盘I/O,你会遇到这样的问题:磁盘并不会因为被多个线程使用而旋转更快 :)


可能,I/O 锁定策略将取决于 Java 实现和底层操作系统。 - Joachim Isaksson
我正在执行一个测试,用例相同 - 从多个线程中读取单个文件。我发现,如果底层存储是SATA硬盘驱动器,则使用多个线程可以提高性能,而如果是SAS驱动器,则会提高性能。这是因为点对点技术还是我的测试有错误? - Andy Dufresne

2
从文件中读取数据是一个天然的序列化过程,假设没有缓存,则意味着您从文件中检索数据的速度有限。即使没有文件锁定(即只读方式打开文件),所有第一次之后的线程都将被阻塞在磁盘读取上,因此您必须使所有其他线程等待,而无论哪个线程在数据变为可用时处于活动状态,都会处理下一个块。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接