个人简介:荡不羁,一生所爱。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后要将返回的值覆盖旧的值。不然不会改变值
如有错误。请大家多多指正