并发编程之ForkJoin使用及原理

以forkJoin的一个使用情景开始吧

public class ForkJoinQuery extends RecursiveTask<Boolean> {
	private static final int THREAD_HOLD = 5000;
	private long start;
    private long end;//需要查询的所有数据的数量
    private PayindentService payindentService;
    private OrderVo orderVo;
    private volatile List<Payindent> resultList;
    
	public ForkJoinQuery(long start, long end, PayindentService payindentService, OrderVo orderVo,
			List<Payindent> resultList) {
		super();
		this.start = start;
		this.end = end;
		this.payindentService = payindentService;
		orderVo.setSize(THREAD_HOLD);
		this.orderVo = orderVo;
		this.resultList = resultList;
	}


	@Override
	protected Boolean compute() {
		boolean flag = (end - start) <= THREAD_HOLD;
		if(flag) {
			//查询
			try {
				List<Payindent> result = payindentService.queryOrder(orderVo);
				resultList.addAll(result);
				return true;
			} catch (Exception e) {
				return false;
			}
			
		}else {
			long mid = (start + end) / 2;
			ForkJoinQuery left = new ForkJoinQuery(start, mid, payindentService, orderVo, resultList);
			//另外一页的数据
			invokeAll(left);
			orderVo.setPage(orderVo.getPage());
			ForkJoinQuery right = new ForkJoinQuery(mid + 1, end, payindentService, orderVo, resultList);
			//调用子任务
			invokeAll(right);
			//将左右的子任务的结果汇聚到返回集合中
			return left.join() && right.join();
		}
		
	}
	
}
try {
			//并行查询
			ForkJoinPool pool = ForkJoinPool.commonPool();
			ForkJoinQuery queryTask = new ForkJoinQuery(0L, sum, payindentService, orderVo, resultList);
			flag = pool.invoke(queryTask);
			pool.shutdown();
			pool.awaitTermination(1, TimeUnit.MINUTES);
		} catch (Exception e) {
			return ResultBean.error();
		}
  • 这里ForkJoinPool提交任务有两种方式,submit和invoke;invoke是同步执行,调用之后阻塞到任务执行完成;submit是异步执行,只有在Future调用get的时候会阻塞;这里的场景需要拿到结果才能进行,所以使用了invoke方法。
  • 在ForkJoinQuery类中继承自RecursiveTask,其实还可以继承自RecursiveAction,不同之处在于前者有返回值,后者没有返回值。

以上是一个简单的使用案例,仅供参考;下面就个人理解,简单阐述下对源码的理解。
ForkJoin的使用从ForkJoinPool开始,获取实例的方法有:

 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();
    }

但是作者推荐使用的是通过commonPool()方法获取

ForkJoinPool pool = ForkJoinPool.commonPool();

这种方式获取的实例是进程共享的(ForkJoinPools中的工作线程在闲置时会被缓慢回收,并在随后需要使用时被恢复),原因看源码:

 /**
     * Common (static) pool. Non-null for public use unless a static
     * construction exception, but internal usages null-check on use
     * to paranoically avoid potential initialization circularities
     * as well as to simplify generated code.
     */
    static final ForkJoinPool common;
=======================
common = java.security.AccessController.doPrivileged
            (new java.security.PrivilegedAction<ForkJoinPool>() {
                public ForkJoinPool run() { return makeCommonPool(); }});

在makeCommonPool()方法中获取实例的方式

return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
                                "ForkJoinPool.commonPool-worker-");
 /**
     * Creates a {@code ForkJoinPool} with the given parameters, without
     * any security checks or parameter validation.  Invoked directly by
     * makeCommonPool.
     */
    private ForkJoinPool(int parallelism,
                         ForkJoinWorkerThreadFactory factory,
                         UncaughtExceptionHandler handler,
                         int mode,
                         String workerNamePrefix) {
        this.workerNamePrefix = workerNamePrefix;
        this.factory = factory;
        this.ueh = handler;
        this.config = (parallelism & SMASK) | mode;
        long np = (long)(-parallelism); // offset ctl counts
        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
    }

从这个私有的构造函数中能够工作线程和工作队列,工作线程由ForkJoinWorkerThreadFactory 创建,子任务存放在WorkQueue队列中;通过poolTask()方法从对列中获取任务。

 protected static ForkJoinTask<?> pollTask() {
        Thread t; ForkJoinWorkerThread wt;
        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
            (wt = (ForkJoinWorkerThread)t).pool.nextTaskFor(wt.workQueue) :
            null;
    }
/**
     * 获取并移除本地任务或者窃取的任务
     *
     * @return a task, if available
     */
    final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
        for (ForkJoinTask<?> t;;) {
            WorkQueue q; int b;
            if ((t = w.nextLocalTask()) != null)
                return t;
            if ((q = findNonEmptyStealQueue()) == null)
                return null;
            if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
                return t;
        }
    }

怎样获取任务已经清楚了,那再拆分任务后怎样将任务放进队列中的呢,RecursiveTask或者RecursiveAction类通过fork()方法对任务进行拆分,同时将子任务放进队列中。

 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;
    }

 final void push(ForkJoinTask<?> task) {
            ForkJoinTask<?>[] a; ForkJoinPool p;
            int b = base, s = top, n;
            if ((a = array) != null) {    // ignore if queue removed
                int m = a.length - 1;     // fenced write for task visibility
                U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
                U.putOrderedInt(this, QTOP, s + 1);
                if ((n = s - b) <= 1) {
                    if ((p = pool) != null)
                        p.signalWork(p.workQueues, this);
                }
                else if (n >= m)
                    growArray();
            }
        }

任务的执行方式在案例中已经提过,就不再赘述。到这里任务的产生,执行都已了解,子任务执行后结果如何整合,通过RecursiveTask的join方法进行整合;通过每个子任务的执行结果递归整合到一起就是最终的结果了。

public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }
public final V getRawResult() {
        return result;
    }
/**
     * Implements execution conventions for RecursiveTask.
     */
    protected final boolean exec() {
        result = compute();
        return true;
    }

当然在ForkJoin框架的解释还是由欠缺的,有待后续补充。(队列的创建,线程工作监控,双端队列之任务窃取等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值