jdk并发工具l类Fork&Join框架

概述

  • Fork/Join框架是jdk1.7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。其思想和MapReduce的思想非常类似。对于任务的分割,要求各个子任务之间相互独立,能够并行独立地执行任务,互相之间不影响。

运行流程图

ForkJoinPool任务调度器

ForkJoinPoolForkJoin框架中的任务调度器,和ThreadPoolExecutor一样实现了自己的线程池,提供了三种调度子任务的方法:

  • execute:异步执行指定任务,无返回结果;
  • invoke、invokeAll:异步执行指定任务,等待完成才返回结果;
  • submit:异步执行指定任务,并立即返回一个Future对象;

fork-join框架中的实际的执行任务类,有以下两种实现,一般继承这两种实现类即可。

  • RecursiveAction:用于无结果返回的子任务;
  • RecursiveTask:用于有结果返回的子任务;

有返回结果的任务

package com.example.lock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

import com.alibaba.fastjson.JSONObject;

public class ForkJoinTask2 extends RecursiveTask<List<JSONObject>>{
	private static final long serialVersionUID = 1L;
	

	//任务切割阈值
	private static final int THRESHOLD =5;
	private   int start;
	private   int end;
	private  List<JSONObject> list;

	public ForkJoinTask2(int start,int end,List<JSONObject> list){
		this.start=start;
		this.end=end;
		this.list=list;
	}
	 
	@Override
	protected List<JSONObject> compute() {
	System.out.println("进入并行任务处理方法...........................");
	   List<JSONObject> lists=new ArrayList<JSONObject>();
		//首先需要判断任务是否小于等于阈值1000  ,如果是就直接执行任务。
		if(end-start<=THRESHOLD){
			for (int i = start; i <=end; i++){
				lists.add(list.get(i));
			}
			System.out.println(Thread.currentThread().getName()+"处理  "+start+"到"+end+"页的数据处理任务");
			return lists;
		}else{
			/**
			 * 否则分割成两个子任务
			 * 每个子任务在调用fork方法时,又会进入compute方法,看看当前子任务是否需要继续分割成子任务,
			 * 如果不需要继续分割,则执行当前子任务并返回结果。使用join方法会阻塞并等待子任务执行完并得到其结果。
			 */
			int mid=(start+end)/2;
			ForkJoinTask2 task1=new ForkJoinTask2(start, mid,list);
			task1.fork();
	
			ForkJoinTask2 task2=new ForkJoinTask2(mid+1, end,list);
			task2.fork();
			lists.addAll(task1.join());
			lists.addAll(task2.join());
		
		
			return lists;
			
		}
		
	}
	 public static void main(String[] args) {
		 long start=System.currentTimeMillis();
		 ForkJoinPool poll=new ForkJoinPool();
		 List<JSONObject> listCount=new ArrayList<>();
		 for (int i = 0; i <=20; i++) {
		     JSONObject obj= new JSONObject();
		      obj.put("id", i);
			  listCount.add(obj);
		 }
		 System.out.println("数据总大小"+listCount.size());
		 List<JSONObject> sumList=poll.invoke(new ForkJoinTask2(0, 19,listCount));
		 
		 System.out.println("并行处理返回结果:"+sumList.size());
		 sumList.forEach(System.out::println);
		 System.out.println(System.currentTimeMillis()-start+"ms");
	}
}

无返回值的任务

public class RaskDemo extends RecursiveAction {

	 /**
     *  每个"小任务"最多只打印20个数
      */
    private static final int MAX = 5;

    private int start;
    private int end;
    private List<String> list;
    public RaskDemo(int start, int end,List<String> list) {
        this.start = start;
        this.end = end;
        this.list=list;
    }

    @Override
    protected void compute() {
        //当end-start的值小于MAX时,开始打印
        if((end-start) < MAX) {
            for(int i= start; i<end;i++) {
             System.out.println(Thread.currentThread().getName()+"list.get(i);的值"+list.get(i));
            }
        }else {
        	System.out.println("任务拆分执行");
            // 将大任务分解成两个小任务
            int middle = (start + end) / 2;
            RaskDemo left = new RaskDemo(start, middle,list);
            RaskDemo right = new RaskDemo(middle, end,list);
            left.fork();
            right.fork();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
    	 // 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
    	List<String> lists=Arrays.asList("1","2","3","4","5","6","7","8","9","10","11","12","13","14","15");
        ForkJoinPool forkJoinPool = new ForkJoinPool();
          
        // 提交可分解的PrintTask任务
        forkJoinPool.submit(new RaskDemo(0, lists.size(),lists));

        //阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
//        forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);
      
        // 关闭线程池
//        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(30, TimeUnit.SECONDS);
        System.out.println("dadasdsa");
	}
}

jdk1.8中的并行流就是for join的实现原理

fork-Join 与传统线程池的区别

  • 采用 “工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
  • 相对于一般的线程池实现,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态,
  • 而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能。

工作窃取算法

  • 工作窃取算法是指某个线程从其它任务里窃取任务来执行,为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务的线程永远从双端队列头部拿任务执行,而窃取任务的线程永远从双端队列尾部拿任务执行。
  • 优点:充分利用线程进行并行计算,减少线程简单竞争
  • 缺点:在某些情况下还是存在竞争。比如双端队列里只有一个任务时。并且该算法会消耗了更多的系统资源,比如创建了多个线程和多个双端队列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值