前言
JUC里面的fork/join
框架,很适合这个情景。
本来我想着在CSDN上随便找一个别人写的就算了,结果居然没发现有用fork/join的。
网上大多数都是直接基于原子操作,记录线程是否全部执行完成,然后向线程池提交一个新线程处理。
只能说挺搞笑的。没想到我居然变成了第一人。
果然靠别人是靠不住的,还是靠自己。
fork/join
是什么我就不介绍了,有兴趣自己百度。
我已经封装好了框架,各位看官直接使用就可以。
代码
package simon.webvpn.three;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.function.Function;
/**
* Author: Simon
* Time: @2023/03/11 20:17:19
*/
public class Faker {
// 判断是否为文件
public static final Function<File, Boolean> Function_isFile = File::isFile;
private final ForkJoinPool forkjoinPool;
public Faker() {
this(Runtime.getRuntime().availableProcessors()-1);
}
public Faker(int threshold) {
forkjoinPool = new ForkJoinPool(threshold);
}
/**
* 获取所有文件
*
* @param rootFile 根文件夹
* @return 文件集合
* @throws ExecutionException 执行出错
* @throws InterruptedException 执行被打断
*/
public ArrayList<File> getAllFile(File rootFile) throws ExecutionException, InterruptedException {
FakerTask task = new FakerTask(rootFile, Function_isFile);
//执行一个任务
Future<ArrayList<File>> result = forkjoinPool.submit(task);
return result.get();
}
/**
* 处理所有文件/文件夹
*
* @param rootFile 根文件夹
* @param function 处理方法
* @return 处理后的文件/文件夹
* @throws ExecutionException 执行出错
* @throws InterruptedException 执行被打断
*/
public ArrayList<File> computeAllFile(File rootFile, Function<File, Boolean> function) throws ExecutionException, InterruptedException {
FakerTask task = new FakerTask(rootFile, function);
//执行一个任务
Future<ArrayList<File>> result = forkjoinPool.submit(task);
return result.get();
}
private static class FakerTask extends RecursiveTask<ArrayList<File>> {
private final File rootFile;
private final Function<File, Boolean> function;
public FakerTask(File rootFile, Function<File, Boolean> function) {
this.rootFile = rootFile;
this.function = function;
}
@Override
protected ArrayList<File> compute() {
// 如果是文件,直接返回
if (function.apply(rootFile)) {
ArrayList<File> files = new ArrayList<>();
files.add(rootFile);
return files;
}
ArrayList<File> bak = new ArrayList<>();
File[] files = rootFile.listFiles();
if (files == null) {
// 这里是 极 特 殊 情 况
// 某种特殊的文件会出现这种问题
return null;
}
FakerTask[] tasks = new FakerTask[files.length];
// 逐个执行
for (int i = 0; i < files.length; i++) {
tasks[i] = new FakerTask(files[i],function);
tasks[i].fork();
}
// 获取子任务的结果
for (FakerTask task : tasks) {
ArrayList<File> join = task.join();
if (join != null)
bak.addAll(join);
}
return bak;
}
}
}
使用
其实这个框架是支持在遍历时对每个文件/文件夹进行处理,再返回结果的
假如你只需要获取文件夹下所有文件,代码如下
Faker faker = new Faker();
File d = new File("");
ArrayList<File> files = faker.getAllFile(d);
假如你想要获取文件夹下所有以.png
结尾的文件,代码如下
Faker faker = new Faker();
File d = new File("");
ArrayList<File> files = faker.computeAllFile(d, new Function<File, Boolean>() {
@Override
public Boolean apply(File file) {
return file.isFile()&&file.getName().endsWith(".png");
}
});
当然这个是支持kotlin的DSL或者普通的lambda进行简化的。
各位使用的时候会有IDEA的提示,我就不多说了
效果
这是扫描我D盘的结果
第一行是传统递归获取到的文件数量
第二行是它需要的时间,去掉后3位就是秒
第一行是Faker获取到的文件数量
第二行是它需要的时间,去掉后3位就是秒
效果很显著