Java并行框架之Fork/Join 应用

2,184 阅读3分钟

个人简介:荡不羁,一生所爱。Java耕耘者(微信公众号ID:Java耕耘者),欢迎关注。可获得2000G详细的2020面试题的资料

一、适用场景

Fork/Join框架是Java提供用于执行并行任务的框架,即把一个大任务拆分分割为多个小任务,最终将小任务的执行结果汇总得到大任务的结果。

大致如下:

二、代码演示

代码调用:PlEquBO cond = new PlEquBO();
......(省略) 
//cond 赋值List<PlEqugrpBO>  
List= ... ....(省略)  ;
//获取值Map<String,BigDecimal> equCapaMap = new HashMap<String,BigDecimal>();
ForkJoinPool forkJoinPool = new ForkJoinPool();
//ForkJoinPool:用来执行Task,或生成新的ForkJoinWorkerThread,
//执行 ForkJoinWorkerThread 间的 work-stealing 逻辑。
//ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。
SingleForkTask task = new SingleForkTask(cond,List);
//SingleForkTask :执行的大任务try{         
equCapaMap = forkJoinPool.invoke(task);
// 同步,返回结果
//ForkJoinPool 使用submit 或 invoke 提交的区别:
//invoke是同步执行,调用之后需要等待任务完成,才能执行后面的代码;
//submit是异步执行,只有在Future调用get的时候会阻塞。
 }
catch(EnhanceException enEx)
{
//EnhanceException 自定义运行时异常           
 throw enEx; 
}
catch(Exception ex){            
throw ex;     
 } 
forkJoinPool.shutdown(); 
forkJoinPool.awaitTermination(1,TimeUnit.HOURS);

SingleForkTask.java

public class SingleForkTask extends RecursiveTask<Map<String,BigDecimal>>
{ 
 //这里继承的是RecursiveTask,适用于有返回值的场景  
//还可以继承RecursiveAction,适合于没有返回值的场景  
private static final long serialVersionUID = 1L;  
//调用接口  private PlCapaServiceInterface PlCapaService;   
 //设置阀值,超过这个数,则进行拆分  private static final int THRESHOLD = 50; 
 //任务需要用的参数A  private PlEquBO plEquBO;    
//任务需要用的参数B  private List<PlEqugrpBO>  list;   
 public SingleForkTask(PlEquBO plEquBO,List<PlEqugrpBO>  list)
{    
this.plEquBO = plEquBO;    
this.list = list;  
}    
@Override  protected Map<String,BigDecimal> compute()
 {   
 Map<String,BigDecimal> result = new HashMap<String,BigDecimal>();    
//判断是否超过拆分的阀值    boolean canCompute = list.size() <= THRESHOLD;    
PlCapaService = findBean();   
 if(canCompute){      
BigDecimal sigleEquResult = BigDecimal.ZERO;     
 for(PlEqugrpBO cond : list){        
sigleEquResult = BigDecimal.ZERO;        
plEquBO.setEquNo(cond.getEquNo());       
 try {          
sigleEquResult = PlCapaService.getSingleResult(plEquBO);          
//获取值        
} 
catch (EnhanceException enEx) 
{
//EnhanceException extends RuntimeException           
throw enEx;        
} 
catch(Exception ex){         
 throw new RuntimeException(ex.getMessage());       
 }       
 result.put(cond.getEquNo(), sigleEquResult);    
  }   
 }
else{      
//超过需要拆分的阀值,则拆分为两份      
List<PlEqugrpBO> leftList = list.subList(0, THRESHOLD);     
 List<PlEqugrpBO> rightList = list.subList(THRESHOLD, list.size());     
 SingleForkTask leftTask = new SingleForkTask(plEquBO,leftList);     
 SingleForkTask rightTask = new SingleForkTask(plEquBO,rightList);    
  //fork:执行子任务的调用     
 leftTask.fork();     
 rightTask.fork();     
 //join:获取任务的结果     
 Map<String,BigDecimal> leftResult = leftTask.join();     
 Map<String,BigDecimal> rightResult = rightTask.join();           
 //若无返回值和无异常抛出的情况,则可以考虑invokeAll(leftTask, rightTask);      
leftResult.putAll(rightResult);    
  result.putAll(leftResult);   
 }   
 return result; 
 }
  public static PlCapaServiceInterface   findBean() 
{    
return EjbBean.findBean(PlCapaServiceInterface.class);
  }
}

三、框架设计

Fork/Join中两个重要的类:

1、ForkJoinTask:使用该框架,需要创建一个ForkJoin任务,它提供在任务中执行fork和join操作的机制。一般情况下,我们并不需要直接继承ForkJoinTask类,只需要继承它的子类,它的子类有两个:

a、RecursiveAction:用于没有返回结果的任务。

b、RecursiveTask:用于有返回结果的任务。

四、Fork/Join:JAVA的并行框架总结

Fork/Join:JAVA的并行框架,一个大任务划分成多个小任务(个人理解相当于分治的思想),其执行的类要实现ForkJoinTask接口下的实现类(RecursiveTask/RecursiveAction),在执行该类的时候需要使用ForkJoinPool来执行,其中的invoke方法是同步方法,execute是异步方法,调用task的join()获取到结果。其使用UNSAFE类来完成线程安全

CountDownLatch:很多说法就是相当于发令枪,当初始化的时候传入一个int的参数。该参数变为0的时候才是执行await()方法后面的代码,当执行一次countDown方法就会减去一次。该类的底层实现是内部类,该类继承了AQS来实现的线程安全

CyclicBarrier:一个可循环使用的功能类似于CountDownLatch的屏障,该类会让所有的线程都执行到屏障的时候才会一起放行所有的线程,其使用了可重入锁来实现的线程同步。该类有一个构造方法可以传入一个Runnable,该类会在所有的线程都执行完毕的时候被执行

Semaphore:一个信号量。控制某一资源被多少的线程同时拿到,其使用继承AQS的内部类完成线程安全

Exchanger:两个线程之间的数据交换,使用CAS来实现线程安全,exchange后要将返回的值覆盖旧的值。不然不会改变值

如有错误。请大家多多指正