java多线程之fork/join

来源:互联网 发布:apk软件破解论坛 编辑:程序博客网 时间:2024/06/07 03:32

1、fork join是什么?

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

fork join流程图

2、怎么使用fork/join


3、工作窃取算法

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

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

工作窃取算法优缺点
优点:充分利用线程进行并行计算,并减少了线程间的竞争。
缺点:1、在某些情况下还是存在竞争,比如双端队列里只有一个任务时。
   2、消耗了更多的系统资源,比如创建多个线程和多个双端队列。

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的实现方式 */@Overrideprotected 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;}@Overrideprotected 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();//关闭线程池}}}




原创粉丝点击