1、fork join是什么?
Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
fork join流程图
2、怎么使用fork/join
3、工作窃取算法
工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下:
那么为什么需要使用工作窃取算法呢?假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如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();//关闭线程池
}
}
}