Fork/Join的应用

版权声明:原创文章,禁止转载,违者必究 https://blog.csdn.net/crazyzxljing0621/article/details/78863510

Fork/Join

JDK1.7开始提供的在JUC包下的并行任务框架。
在多核机器上有显著的效果,主要核心工作窃取 
拥有工作窃取算法,空闲线程会帮助其他有任务的线程处理任务队列

应用概述

查询某个文件夹路径下容量最大的文件

常用方法:
1. 单线程依次遍历每层目录及文件,记录并对比文件大小
    写法简单,效率不高
2.多线程线程池遍历每层目录及文件
   线程数量,每条线程遍历的目标,需要控制,很容易出现空闲线程

这里我们就可以使用Fork/Join并行处理此任务,

将任务分成多个子任务,执行后分别返回结果,合并结果到最终返回。


package com.nf;

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

public class ReckonTask extends RecursiveTask<File> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	File path;
	Filter filter; 

	public ReckonTask(File path, Filter filter) {
		this.path = path;
		this.filter = filter;
	} 

	@Override
	protected File compute() {
		File maxFile = null;
		File[] listFile = path.listFiles();
		for (int i = 0; i < listFile.length; i++) {
			File nFile = listFile[i]; 
			if (nFile.isDirectory()) {
				ReckonTask rt = new ReckonTask(nFile, filter);
				rt.fork();
				maxFile = filter.addFilter(maxFile, rt.join()); 
			} else {
				maxFile = filter .addFilter(maxFile, nFile);
			}
		} 
		return maxFile;
	}
}
package com.nf;

import java.io.File;

public class MaxFilter implements Filter {

	@Override
	public File addFilter(File f1, File f2) {
		if (f1 == null)
			return f2;
		else if (f2 == null)
			return f1;
		else {
			return f1.length()> f2.length()?f1:f2;
		}
	} 
}
package com.nf;

import java.io.File;

public interface Filter {

	public File addFilter(File f1, File f2); 
}
package com.nf;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;

public class Main {

	public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
		
		File path = new File("E:\\eclipse");
		ForkJoinPool fjp = new ForkJoinPool();
		long s = System.currentTimeMillis();
		ReckonTask rt = new ReckonTask(path, new MaxFilter()); 
		ForkJoinTask<File> fjt = fjp.submit(rt);
		File result = fjt.get();
		System.out.printf("文件目录: %s\n文件大小: %.2fMB\n消耗时间: %sms\n", result.getAbsolutePath(),
				result.length() / 1024 / 1024f, System.currentTimeMillis() - s);
	}
}
 
文件目录: E:\eclipse\plugins\org.eclipse.jdt.ui_3.12.2.v20160929-0804.jar
文件大小: 11.07MB
消耗时间: 758ms
上面代码中当我发现是文件夹类型时,就建立一个任务去处理他。当然这个eclipse下有很多文件夹,层次也很深。当某个线程完成自己任务时候,会协助其他线程来进行任务。这样就应用了工作窃取算法。

当然为什么说是并行呢。因为在ForkJoinPool创建的时候,我们可以看到,它根据CPU的处理器个数选择了
 /**
     * Creates a {@code ForkJoinPool} with parallelism equal to {@link
     * java.lang.Runtime#availableProcessors}, using the {@linkplain
     * #defaultForkJoinWorkerThreadFactory default thread factory},
     * no UncaughtExceptionHandler, and non-async LIFO processing mode.
     *
     * @throws SecurityException if a security manager exists and
     *         the caller is not permitted to modify threads
     *         because it does not hold {@link
     *         java.lang.RuntimePermission}{@code ("modifyThread")}
     */
    public ForkJoinPool() {
        this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
             defaultForkJoinWorkerThreadFactory, null, false);
    }
关于并行数不能超过32767,这个是ForkJoinPool中预设的阈值。是不可变的
static final int MAX_CAP      = 0x7fff
 private static int checkParallelism(int parallelism) {
        if (parallelism <= 0 || parallelism > MAX_CAP)
            throw new IllegalArgumentException();
        return parallelism;
    }
如果并行数超出或设置错误会抛出IllegalArgumentException的

就这样把,没有太复杂,具体的大家可以看看API,我认为在实际应用场景中主要需要思考就是在于合并任务时候的操作以及良好的开闭设计






展开阅读全文

没有更多推荐了,返回首页