多线程扫描文件夹耗时方法分析

在这里Java的方法中有线程递归,不懂得用什么方法求运行时间遇到一个有趣的问题,多线程扫描文件夹求运行时间。一般这种扫描文件夹耗时好像都是用的递归遍历一下进行计时,头一次看到这种一个文件夹一个线程的,这里按照原问题楼主的想法试着解决一下。

刚开始我是采用静态变量,共享总耗时,把每个run方法耗时都加上。

package com.brewin.codetuning.test;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadDemo02
{
    public static void main(String args[]) {
        File file=new File("D:\\AA");
        MyThread mt1 = new MyThread("线程1",file);
        mt1.start();
    }
}

class MyThread extends Thread
{
    private String name;
    private File file;
    public static int sum;
    static {
        sum = 0;
    }

    public MyThread(String name,File file) {
        this.name = name;
        this.file=file;
    }

    @Override
    public void run() {
        long startTime =  System.currentTimeMillis();
        File[] files=file.listFiles();
        if(files!=null &&files.length>0) {
            for(File f:files) {
                if(f.isDirectory()) {
                    new MyThread(f.getName(),f).start();
                }
//                打印文件名不方便看最后的耗时打印,注释掉
//                else {
//                    System.out.println(Thread.currentThread().getName()+":"+f);
//                }
            }
        }
//        try {
//            Thread.sleep(5000);
//        }
//        catch (InterruptedException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
        long endTime =  System.currentTimeMillis();
        long usedTime = (endTime-startTime);
        sum+=usedTime;
        System.out.println(name + " 运行结束,耗时: "+usedTime+" ms,sum现为:"  +MyThread.sum+" ms");
    }

}

控制台打印的什么呢?下面截取最后一部分

storage 运行结束,耗时: 0 ms,sum现为:3156 ms
x509 运行结束,耗时: 1 ms,sum现为:3157 ms
implementations 运行结束,耗时: 1 ms,sum现为:3158 ms
implementations 运行结束,耗时: 1 ms,sum现为:3159 ms

但是实际并没有用到3秒多,很快就结束了。

下面是加了Thread.sleep(5000)的结果,更明显一些:

xs 运行结束,耗时: 5004 ms,sum现为:2611580 ms
implementations 运行结束,耗时: 5001 ms,sum现为:2606576 ms
traversers 运行结束,耗时: 5001 ms,sum现为:2616581 ms

多线程是并发执行,所以这个思路是错误的,不能计算他们的总和,而是要计算他们的最开始的时间和最后的时间。改变思路,在最开始时初始化一个开始时间,后面每次线程结束后都更新一下最后时间,求他们之间的差值就可以了。

package com.brewin.codetuning.test;

import java.io.File;

public class ThreadDemo02
{
    public static void main(String args[]) {
        File file=new File("D:\\AA");
        MyThread mt1 = new MyThread("线程1",file);
        mt1.start();
    }
}

class MyThread extends Thread
{
    private static long startTime;
    private String name;
    private File file;

    static {
        startTime =  System.currentTimeMillis();
    }

    public MyThread(String name,File file) {
        this.name = name;
        this.file=file;
    }

    @Override
    public void run() {
        File[] files=file.listFiles();
        if(files!=null &&files.length>0) {
            for(File f:files) {
                if(f.isDirectory()) {
                    new MyThread(f.getName(),f).start();
                }
//                打印文件名不方便看最后的耗时打印,注释掉
//                }else {
//                    System.out.println(Thread.currentThread().getName()+":"+f);
//                }
            }
        }
        long usedTime = System.currentTimeMillis()-startTime;
        System.out.println(name + " 运行结束,已耗时: "+usedTime+" ms");
    }
}

控制台最后一段打印如下:

io 运行结束,已耗时: 164 ms
proxy 运行结束,已耗时: 164 ms
dom 运行结束,已耗时: 164 ms
serialize 运行结束,已耗时: 165 ms
fsm 运行结束,已耗时: 165 ms
rmi 运行结束,已耗时: 166 ms
spi 运行结束,已耗时: 166 ms
tree 运行结束,已耗时: 166 ms
graph 运行结束,已耗时: 166 ms
util 运行结束,已耗时: 166 ms
utils 运行结束,已耗时: 167 ms
protocol 运行结束,已耗时: 167 ms
implementations 运行结束,已耗时: 167 ms
http 运行结束,已耗时: 167 ms
content 运行结束,已耗时: 167 ms
resolver 运行结束,已耗时: 167 ms
util 运行结束,已耗时: 168 ms
identity 运行结束,已耗时: 168 ms
keyvalues 运行结束,已耗时: 168 ms
x509 运行结束,已耗时: 169 ms
xs 运行结束,已耗时: 169 ms
serializer 运行结束,已耗时: 169 ms
util 运行结束,已耗时: 169 ms
xs 运行结束,已耗时: 169 ms
util 运行结束,已耗时: 169 ms
validation 运行结束,已耗时: 170 ms
undo 运行结束,已耗时: 170 ms
traversers 运行结束,已耗时: 170 ms
util 运行结束,已耗时: 170 ms
namingutil 运行结束,已耗时: 170 ms
exceptions 运行结束,已耗时: 170 ms
CORBA 运行结束,已耗时: 171 ms
xpath 运行结束,已耗时: 171 ms
transforms 运行结束,已耗时: 171 ms
utils 运行结束,已耗时: 171 ms
params 运行结束,已耗时: 171 ms
encryption 运行结束,已耗时: 171 ms
signature 运行结束,已耗时: 171 ms
output 运行结束,已耗时: 171 ms
implementations 运行结束,已耗时: 172 ms
closure 运行结束,已耗时: 172 ms
concurrent 运行结束,已耗时: 172 ms
orbutil 运行结束,已耗时: 172 ms
helper 运行结束,已耗时: 172 ms
keyresolver 运行结束,已耗时: 172 ms
storage 运行结束,已耗时: 172 ms
implementations 运行结束,已耗时: 173 ms
implementations 运行结束,已耗时: 173 ms
resolver 运行结束,已耗时: 173 ms
implementations 运行结束,已耗时: 173 ms
models 运行结束,已耗时: 174 ms
implementations 运行结束,已耗时: 174 ms
opti 运行结束,已耗时: 174 ms
text 运行结束,已耗时: 174 ms
rtf 运行结束,已耗时: 174 ms
utils 运行结束,已耗时: 175 ms
giopmsgheaders 运行结束,已耗时: 175 ms
reference 运行结束,已耗时: 175 ms
regex 运行结束,已耗时: 175 ms
threadpool 运行结束,已耗时: 175 ms
res 运行结束,已耗时: 176 ms
html 运行结束,已耗时: 177 ms
parser 运行结束,已耗时: 177 ms

看到原问题楼主有提到CountDownLatch ,找来文章看了一下,不是很适合这里,CountDownLatch计数器的初始大小要跟任务数的大小一致(跟线程数无关),每执行一次任务,计数器减一(countDown),await()方法会一直阻塞主线程,直到计数器的值减为0,才会释放锁,如此便可以达到确保所有任务都完成才继续下一步的效果。

但是本文场景是不断递归new出新线程,并且本文场景中要求任务数和线程数是相等的,无法确定任务数,就没办法初始化CountDownLatch 大小。

想要用CountDownLatch 的话只能加个统计方法,获得文件夹数量,如下:

package com.brewin.codetuning.test;

import java.io.File;
import java.util.concurrent.CountDownLatch;

public class ThreadDemo02
{
    static int count;
    static {
        count = 0;
    }
    public static void main(String args[]) {
        File file = new File("D:\\AA");
        countNumberOfFolders(file);
        long startTime = System.currentTimeMillis();
        MyThread mt1 = new MyThread("线程1", file);
        mt1.start();
        try {
            MyThread.latch.await();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        long usedTime = System.currentTimeMillis() - startTime;
        System.out.println("耗时: " + usedTime + " ms");

    }

    /**
     *  统计文件夹数量
     *  @param f    
     * @exception/throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public static void countNumberOfFolders(File f) {
        count++;
        File[] files = f.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory()) {
                    countNumberOfFolders(file);
                }

            }
        }
    }

}

class MyThread extends Thread
{
    private String name;
    private File file;
    static CountDownLatch latch;
    static {
        latch = new CountDownLatch(ThreadDemo02.count);
    }

    public MyThread(String name, File file) {
        this.name = name;
        this.file = file;
    }

    @Override
    public void run() {
        File[] files = file.listFiles();
        if (files != null && files.length > 0) {
            for (File f : files) {
                if (f.isDirectory()) {
                    new MyThread(f.getName(), f).start();
                }
//                打印文件名不方便看最后的耗时打印,注释掉
//                }else {
//                    System.out.println(Thread.currentThread().getName()+":"+f);
//                }
            }
        }
        System.out.println(name + " 运行结束");
        latch.countDown();
    }
}

结果如下:

http 运行结束
output 运行结束
dom 运行结束
compiler 运行结束
util 运行结束
耗时: 350 ms

可以看到确实是等到所有的线程执行结束后才进行的下一步。

如上 结题

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!