【发布时间】:2018-08-02 10:01:18
【问题描述】:
我想在 Java 8 中尝试 ForkJoinPool,所以我编写了一个小程序来搜索给定目录中名称包含特定关键字的所有文件。
计划:
public class DirectoryService {
public static void main(String[] args) {
FileSearchRecursiveTask task = new FileSearchRecursiveTask("./DIR");
ForkJoinPool pool = (ForkJoinPool) Executors.newWorkStealingPool();
List<String> files = pool.invoke(task);
pool.shutdown();
System.out.println("Total no of files with hello" + files.size());
}
}
class FileSearchRecursiveTask extends RecursiveTask<List<String>> {
private String path;
public FileSearchRecursiveTask(String path) {
this.path = path;
}
@Override
protected List<String> compute() {
File mainDirectory = new File(path);
List<String> filetedFileList = new ArrayList<>();
List<FileSearchRecursiveTask> recursiveTasks = new ArrayList<>();
if(mainDirectory.isDirectory()) {
System.out.println(Thread.currentThread() + " - Directory is " + mainDirectory.getName());
if(mainDirectory.canRead()) {
File[] fileList = mainDirectory.listFiles();
for(File file : fileList) {
System.out.println(Thread.currentThread() + "Looking into:" + file.getAbsolutePath());
if(file.isDirectory()) {
FileSearchRecursiveTask task = new FileSearchRecursiveTask(file.getAbsolutePath());
recursiveTasks.add(task);
task.fork();
} else {
if (file.getName().contains("hello")) {
System.out.println(file.getName());
filetedFileList.add(file.getName());
}
}
}
}
for(FileSearchRecursiveTask task : recursiveTasks) {
filetedFileList.addAll(task.join());
}
}
return filetedFileList;
}
}
当目录没有太多子目录和文件时,这个程序可以正常工作,但如果它真的很大,那么它会抛出 OutOfMemoryError。
我的理解是最大线程数(包括补偿线程)是有界的,那么为什么会出现这个错误?我的程序中是否缺少任何内容?
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at java.util.concurrent.ForkJoinPool.createWorker(ForkJoinPool.java:1486)
at java.util.concurrent.ForkJoinPool.tryCompensate(ForkJoinPool.java:2020)
at java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:2057)
at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:390)
at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
at FileSearchRecursiveTask.compute(DirectoryService.java:51)
at FileSearchRecursiveTask.compute(DirectoryService.java:20)
at java.util.concurrent.RecursiveTask.exec(RecursiveTask.java:94)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1107)
at java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:2046)
at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:390)
at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
at FileSearchRecursiveTask.compute(DirectoryService.java:51)
at FileSearchRecursiveTask.compute(DirectoryService.java:20)
at java.util.concurrent.RecursiveTask.exec(RecursiveTask.java:94)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
【问题讨论】:
-
如果
newWorkStealingPool()的作者希望您假设返回的执行程序始终是ForkJoinPool,他们将返回类型声明为这样。因为他们没有,你不应该将返回的对象转换为ForkJoinPool。毕竟,使用工厂方法并假设它总是具有特定的甚至未记录的行为是没有意义的。如果您想毫无疑问地获得一个新的ForkJoinPool,只需使用new ForkJoinPool()... -
主要原因是 RecursiveTask 和 ForkJoinTask 都不是 Callable 或 Runnable 类型,所以我不能调用 ExecutorService 上的方法。我在这里有什么遗漏吗?
-
是的。对于使用
RecursiveTask或ForkJoinTask,您需要一个ForkJoinPool,因此您应该使用ForkJoinPool,或者ForkJoinPool.commonPool(),或者明确地创建一个新的,例如通过new ForkJoinPool()。如果工厂方法声明的返回类型不适合您的任务,请不要将该工厂方法用于您的任务。 -
谢谢,我会记住这一点,但恕我直言,作者也应该考虑对 ForkJoinTask 的支持。
-
为了对称,是的。但最终,与
Executors.newForkJoinPool()(如果存在的话)和new ForkJoinPool()相比,前者的约定是做后者做的事情。
标签: java multithreading java-8 fork-join forkjoinpool