Fork/Join的應用

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,我認爲在實際應用場景中主要需要思考就是在於合併任務時候的操作以及良好的開閉設計






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章