ForkJoin框架(二):ForkJoinTask

1. ForkJoinTask概述

ForkJoin框架概述中已经对ForkJoinTask进行了基本介绍,本文主要从实现的角度剖析ForkJoinTask。简单的说,ForkJoinTask将任务fork成足够小的任务,并发解决这些小任务,然后将这些小任务结果join。这种思想充分利用了CPU的多核系统,使得CPU的利用率得到大幅度提升,减少了任务执行时间。

通常我们会利用ForkJoinTask的fork方法来分割任务,利用join方法来合并任务,因此我们首先以这两个方法作为切入点。

2. 分割子任务-fork

public final ForkJoinTask<V> fork() {
    ((ForkJoinWorkerThread) Thread.currentThread()).pushTask(this);
    return this;
}

该方法很简单,将该任务添加到当前线程维护的队列队首处,返回该任务。这里有三点需要注意的:

  1. fork方法将当前线程强制转换成ForkJoinWorkerThread,通过ForkJoinPool执行ForkJoinTask的线程都是该框架定义的ForkJoinWorkerThread,因此这种转换是正确的。如果不是利用ForkJoinPool线程池执行ForkJoinTask,将Thread强制转换成ForkJoinWorkerThread会抛出ClassCastException异常。
  2. 将任务加到队列后,由ForkJoinPool调度执行该任务。一般情况下,该任务会被队列所在的线程执行,当线程池其他线程空闲的时候,该任务可能被其他线程窃取。
  3. 任务被添加到队列的队首处,即本线程fork的小任务被添加到队首,这隐藏着另一个意思,队列中越早的任务是个较大的任务,刚添加的任务是较小的任务,每次该线程从队首拿更小的任务执行,更小的任务执行完成之后才能join成更大的任务。其他线程从该队列窃取任务的时候会窃取队列最早的任务,即窃取了较大的任务执行。

3. 合并结果-join

假设我们将当前任务t分割成两个任务t1和t2,为了获取任务t的结果,需要等待任务t1和t2的结果,代码片断通常是这种形式:

t1.fork();
t2.fork();
result = t1.join() + t2.join();

join方法正是阻塞等待当前任务执行结束并返回结果。如果任务非正常结束,join方法可能会抛出异常。join结果的时候,如果线程维护的队列头就是该任务,那么直接执行该任务,否则还有更小的任务需要执行,等待该线程执行完该任务。

和很多其他JUC框架类似,ForkJoinTask也有自己的任务执行状态,先学习下ForkJoinTask的几个状态:

volatile int status; 
private static final int NORMAL      = -1;
private static final int CANCELLED   = -2;
private static final int EXCEPTIONAL = -3;
private static final int SIGNAL      =  1;

status是一个volatile变量,表示当前任务的执行状态,它有五个状态,负数表示该任务已经执行完成,非负数表示任务还没有执行完成。其中已经执行完成状态又包括NORMAL、CANCELLED、EXCEPTIONAL三种状态,未完成状态包括初始状态0和SIGNAL。下面详细看下每个状态:

  1. NORMAL:表示任务“正常”完成的状态。
  2. CANCELLED:表示任务“取消”完成的状态。
  3. EXCEPTIONAL:表示任务“异常”完成的状态。注意,以上这三个状态都是“完成”的状态,只是完成的途径不一样。
  4. SIGNAL:有其他任务依赖当前任务,任务结束前,通知其他任务join当前任务的结果。
  5. 0:任务初始状态(正在执行状态),不需要等待子任务完成。

join方法调用了doJoin方法,doJoin方法有一个关键代码片断:

//任务正常完成,设置正常完成状态,通知其他需要join该任务的线程
if (completed)
  return setCompletion(NORMAL);

当前任务完成后,设置该任务的完成状态为NORMAL,并且notifyAll(唤醒)其他在该任务上等待的线程。其他线程被唤醒后会合并该任务的执行结果。既然有notifyAll,那对应的wait在哪里呢?ForkJoinPool调用了ForkJoinTask的tryAwaitDone方法,等待任务完成。

接着详细看下doJoin方法:

private int doJoin() {
    Thread t; ForkJoinWorkerThread w; int s; boolean completed;
    //如果当前线程是ForkJoinWorkerThread
    if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
      //负数表示任务已经完成了,直接返回
      if ((s = status) < 0)
        return s;
      //从队首取出当前任务,如果队首不是该任务,unpushTask返回false
      if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
        try {
          //执行任务,exec是一个抽象方法,为了扩展,留给子类实现
          //exec返回任务是否正常完成
          completed =
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值