1、fork join是什么?

Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

fork join流程图

java 多线程UDP JAVA 多线程 fork_多线程

2、怎么使用fork/join

java 多线程UDP JAVA 多线程 fork_java_02

3、工作窃取算法

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下:


java 多线程UDP JAVA 多线程 fork_java_03


        


        那么为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。(分别从头、尾分别拿)




工作窃取算法优缺点


优点:充分利用线程进行并行计算,并减少了线程间的竞争。


缺点:1、在某些情况下还是存在竞争,比如双端队列里只有一个任务时。






4、继承RecursiveTask方式实现统计文件数目以及单线程方式实现对比


package forkJoin;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
/**
 * 遍历某一个目录下所有文件
 * @author ocean
 *
 */
public class ForkJoinTask extends RecursiveTask<Integer>{

	private static final long serialVersionUID = 1L;

	private List<ForkJoinTask> subTask = new ArrayList<ForkJoinTask>();
	
	private int singleFileTotal = 0;
	private Path dir;
	public ForkJoinTask(Path dir){
		this.dir = dir;
	}
	public ForkJoinTask(){
		
	}
	
	/**
	 * fork join的实现方式
	 */
	@Override
	protected Integer compute() {
		int count = 0;
		try {
			DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dir);
			for(Path path : directoryStream){
				if(Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)){
					subTask.add(new ForkJoinTask(path));
				}else{
					count++;
				}
			}
			
			if(!subTask.isEmpty()){
				for(ForkJoinTask forkJoinTask : invokeAll(subTask)){
					count += forkJoinTask.join();
				}
			}
		} catch (IOException e) {
			return 0;
		}
		return count;
	}
	
	/**
	 * 单线程的实现方式
	 * @param path
	 * @return
	 */
	public int singleThreadStastic(Path path){
		try {
			DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
			for(Path subPath : directoryStream){
				if(Files.isDirectory(subPath, LinkOption.NOFOLLOW_LINKS)){
					singleThreadStastic(subPath);
				}else{
					singleFileTotal++;
				}
			}
		} catch (IOException e) {
			return 0;
		}
		return singleFileTotal;
	}
	
	/**
	 * 测试代码
	 * @param args
	 */
	public static void main(String[] args) {
		//fork join方式实现
		System.out.println("fork join start....");
		long start = System.currentTimeMillis();
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		ForkJoinTask forkJoinTask = new ForkJoinTask(Paths.get("C:/"));
		forkJoinPool.invoke(forkJoinTask);
		int count = forkJoinTask.join();
		System.out.println("fork join耗时"+(System.currentTimeMillis()-start)+"ms,一共"+count+"个文件");
		
		//单线程方式实现
		System.out.println("single thread start....");
		long start1 = System.currentTimeMillis();
		ForkJoinTask forkJoinTask1 = new ForkJoinTask();
		int fileTotal = forkJoinTask1.singleThreadStastic(Paths.get("C:/"));
		System.out.println("单线程耗时"+(System.currentTimeMillis()-start1)+"ms,一共"+fileTotal+"个文件");
		
		/**
		 * 运行结果
		 * fork join start....
		   fork join耗时14036ms,一共131423个文件
		   single thread start....
		         单线程耗时50263ms,一共131423个文件
		 */
	}
}


5、通过RecursiveAction不返回子任务结果方式

package forkJoin;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

public class ForkJoinAsync extends  RecursiveAction{
	public List<Integer> list;
	private final static int maxNum = 100;
	
	public ForkJoinAsync(List<Integer> list) {
		this.list = list;
	}

	@Override
	protected void compute() {
		if(list.size()<maxNum){
			System.out.println("执行一条任务");
		}else{
			System.out.println("拆分任务");
			int half = list.size()/2;
			ForkJoinAsync forkJoinAsync1 = new ForkJoinAsync(list.subList(0, half));
			ForkJoinAsync forkJoinAsync2 = new ForkJoinAsync(list.subList(half, list.size()));
//			forkJoinAsync2.fork();
//			forkJoinAsync1.fork();
			forkJoinAsync1.invoke();
			forkJoinAsync2.invoke();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		List<Integer> list = new ArrayList<Integer>();
		for(int i=0;i<3000;i++){
			list.add(i);
		}
		ForkJoinAsync forkJoinAsync = new ForkJoinAsync(list);
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		forkJoinPool.execute(forkJoinAsync);//非阻塞方式。子任务执行不会影响主线程,主线程继续执行
		forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);
		if(forkJoinAsync.isDone()){//任务完成
			forkJoinPool.shutdown();//关闭线程池
		}
	}
}