多线程扫描文件夹耗时方法分析
在这里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 协议》,转载必须注明作者和本文链接