Java7 ForkJoinPool使用及源码解析

Java7中引入了一种新的并发框架-Fork/Join,Fork/Join采用分治+work-stealing的思想,Fork/Join相教于其他并发框架有其适合的使用场景:如果一个任务能够被分为多个子任务,通过组合这些子任务的结果就能获得最终结果,那么这项任务就适合用Fork/Join模式解决。

一、ForkJoinPool使用示例

如上说述,递归问题比较适合用ForkJoin框架解决,如求Fibonacci数列的第N项值: F(n) = F(n-1) + F(n-2),实现如下:

public class ForkJoinTest {
    
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        Fibonacci task = new Fibonacci(5);
        int ans = pool.invoke(task);
        System.out.println(ans);
    }

    static class Fibonacci extends RecursiveTask<Integer> {
        private int n;
        public Fibonacci(int n) {
            this.n = n;
        }
        @Override
        protected Integer compute() {
            System.out.println(Thread.currentThread().getName());
            if(n <= 2) {
                return 1;
            }
            try{
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Fibonacci f1 = new Fibonacci(n - 1);
            f1.fork();
            Fibonacci f2 = new Fibonacci(n - 2);
            f2.fork();
            int ans = f1.join() + f2.join();
            return ans;
        }
    }
}
---
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-4
5
二、ForkJoinPool解析
  • 2.1 ForkJoinPool构造函数

ForkJoinPool一共有3个共有构造函数,如下:

    public ForkJoinPool() {
        this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),defaultForkJoinWorkerThreadFactory, null, false);
    }
    public ForkJoinPool(int parallelism) {
        this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
    }
    public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }
构造函数参数含义
int parallelism可并行级别,最小为1。Fork/Join框架根据这个级别设定并行执行的线程数,但parallelism不等于线程数。默认构造函数中将改值设为CPU的核数
ForkJoinWorkerThreadFactory factory线程创建工厂。它是一个函数式接口,需要实现一个newThread方法
UncaughtExceptionHandler handler异常处理器。处理任务中抛出的异常,默认为null
boolean asyncMode是否为异步模式,默认为false,即LIFO模式。指定线程任务队列的处理方式,通常有LIFO、FIFO两种模式
  • 2.2 ForkJoinPool的任务提交方法

ForkJoinPool提供了三个任务提交的方法:execute、submit和invoke:

    public void execute(ForkJoinTask<?> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
    }

    public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task;
    }

    public <T> T invoke(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task.join();
    }

execute方法和submit方法大致逻辑相同,都是先对任务做非空校验再讲任务压入ForkJoinPool中的执行队列,唯一不同的是submit会把任务对象本身返回,返回后可以通过get()方法获取执行结果。和execute方法、submit方法不同,invoke方法将任务压入队列后会执行join()方法,阻塞当前线程。

  • 2.3 ForkJoinWorkerThreadFactory与ForkJoinWorkerThread

ForkJoinPool提供了一个线程创建工厂接口并提供了默认实现,通过该工厂可以创建ForkJoinWorkerThread线程

    public static interface ForkJoinWorkerThreadFactory {
        public ForkJoinWorkerThread newThread(ForkJoinPool pool);
    }
    static final class DefaultForkJoinWorkerThreadFactory
        implements ForkJoinWorkerThreadFactory {
        public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
            return new ForkJoinWorkerThread(pool);
        }
    }

ForkJoinWorkerThread主要有两个成员变量,一个是线程所归属的ForkJoinPool线程池,另一个是存放ForkJoinTask的任务队列,可见每个ForkJoinWorkerThread线程都维护一个任务队列,该队列提供LIFO和FIFO两种出队方式。

public class ForkJoinWorkerThread extends Thread {
    final ForkJoinPool pool;                // the pool this thread works in
    final ForkJoinPool.WorkQueue workQueue;
	...
}

public class ForkJoinPool extends AbstractExecutorService {
	static final class WorkQueue {
		final void push(ForkJoinTask<?> task) {
		...
		}
		/**
         * Takes next task, if one exists, in LIFO order.  Call only
         * by owner in unshared queues.
         */
        final ForkJoinTask<?> pop() {	
        ...
        }
        /**
         * Takes next task, if one exists, in FIFO order.
         */
        final ForkJoinTask<?> poll() {
        ...
        } 
	}
}
  • 2.3 ForkJoinPool中其它方法

shutDown(); // 执行该方法后ForkJoinPool不再接受新任务,但队列中未执行的任务和正在执行中的任务仍然可以继续执行

    public void shutdown() {
        checkPermission();
        tryTerminate(false, true);
    }

shutDownNow(); // 停止所有未执行的任务并且拒绝新任务,正在执行中的任务可继续执行

    public List<Runnable> shutdownNow() {
        checkPermission();
        tryTerminate(true, true);
        return Collections.emptyList();
    }

awaitTermination(long timeout, TimeUnit unit); // 阻塞当前线程,直到ForkJoinPool中所有任务执行完毕

public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
...
}
  • 2.4 ForkJoinTask.fork()方法

fork()方法判断当前线程是否是ForkJoinWorkerThread的实例,如果是则将任务压入当前线程的队列中;如果不是则将任务压入ForkJoinPool的公用线程池的执行队列中

    public final ForkJoinTask<V> fork() {
        Thread t;
        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
            ((ForkJoinWorkerThread)t).workQueue.push(this);
        else
            ForkJoinPool.common.externalPush(this);
        return this;
    }
  • 2.4 ForkJoinTask.join()方法
执行步骤为:
a.判断当前任务是否执行完成、取消或异常,如果是则退出
b.如果任务是CountedCompleter类型任务,则通过helpComplet完成当前队列的任务
c.非CountedCompleter任务调用tryRemoveAndExec执行任务
d.如果当前队列为空,则任务可能被偷,通过helpStealer帮助偷取者执行任务
e.再次判断当前任务的状态是否执行完成、取消或异常,如果是则退出
f.判断是否超时,如果deadline不为0且超时则退出
g.执行补偿逻辑,如果补偿成功,则阻塞一段时间
    public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }

    private int doJoin() {
        int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
        return (s = status) < 0 ? s :
            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
            (w = (wt = (ForkJoinWorkerThread)t).workQueue).
            tryUnpush(this) && (s = doExec()) < 0 ? s :
            wt.pool.awaitJoin(w, this, 0L) :
            externalAwaitDone();
    }

	final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
        int s = 0;
        if (task != null && w != null) {
            ForkJoinTask<?> prevJoin = w.currentJoin;
            U.putOrderedObject(w, QCURRENTJOIN, task);
            CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
                (CountedCompleter<?>)task : null;
            for (;;) {
                if ((s = task.status) < 0)
                    break;
                if (cc != null)
                    helpComplete(w, cc, 0);
                else if (w.base == w.top || w.tryRemoveAndExec(task))
                    helpStealer(w, task);
                if ((s = task.status) < 0)
                    break;
                long ms, ns;
                if (deadline == 0L)
                    ms = 0L;
                else if ((ns = deadline - System.nanoTime()) <= 0L)
                    break;
                else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
                    ms = 1L;
                if (tryCompensate(w)) {
                    task.internalWait(ms);
                    U.getAndAddLong(this, CTL, AC_UNIT);
                }
            }
            U.putOrderedObject(w, QCURRENTJOIN, prevJoin);
        }
        return s;
    }
参考
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值