以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框架的解释还是由欠缺的,有待后续补充。(队列的创建,线程工作监控,双端队列之任务窃取等)。