[JAVA学习笔记-51]ForkJoin

来源:互联网 发布:java项目收获总结 编辑:程序博客网 时间:2024/06/03 18:56
【ForkJoin的运行原理简析】
1.CaLL ForkJoinPool.invoke(ForkJoinTask)
2.put the given task into taskqueue of the pool(addSubmission(task);)
3.the a ForkJoinWorkerThread will scan the queue,call ForkJoinTask.doExec(),then call 
  exec() method in concrete-ForkJoinTask like RecursiveTask
4.exec() method in RecursiveTask is like this:
Its an implementation of abstract method in ForkJoinTask;


protected final boolean exec() {
        result = compute();
        return true;
    }

here the method compute() finally been invoked,the compute() should be implemented by user

【ForkJoinPool如何获得 ForkJoinWorkerThread】
See from the constructor of ForkJoinPool.
public ForkJoinPool() {
        this(Runtime.getRuntime().availableProcessors(),
             defaultForkJoinWorkerThreadFactory, null, false);
    }

public ForkJoinPool(
int parallelism,  /*可用的处理器个数,通过Runtime调用native的availableProcessors()获取*/
ForkJoinWorkerThreadFactory factory,/*简单工厂模式,这里用了默认的工作线程工厂*/
Thread.UncaughtExceptionHandler handler,/*工作线程异常终止(不可恢复)时的处理者,一般为null,不指定*/
boolean asyncMode)

/*<最后一个参数的说明>*/
@param asyncMode if true,
     * establishes local first-in-first-out scheduling mode for forked
     * tasks that are never joined. This mode may be more appropriate
     * than default locally stack-based mode in applications in which
     * worker threads only process event-style asynchronous tasks.
     * For default value, use {@code false}.
 
默认是false,使用默认的,基于栈的对fork task调度方式,即最新fork出来的任务,在栈顶,执行完后出栈,将result返回给栈的上一级fork tasks。 如果为true,则使用先进先出的调度方式,在工作线程只处理异步的,事件形式的任务时,用这种方式更合适(像消息队列,事件队列一样处理)。
 
/*构造函数解析*/
{
        checkPermission();
        if (factory == null)
            throw new NullPointerException();
        if (parallelism <= 0 || parallelism > MAX_ID)
            throw new IllegalArgumentException();
        this.parallelism = parallelism;
        this.factory = factory;
        this.ueh = handler;
        this.locallyFifo = asyncMode;
        long np = (long)(-parallelism); // offset ctl counts
        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
        this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];   /*创建一个 ForkJoinTask 数组作为任务队列,默认8*/
        // initialize workers array with room for 2*parallelism if possible   /*为什么工作线程数,要是处理器个数的2倍?*/
        int n = parallelism << 1;
        if (n >= MAX_ID)
            n = MAX_ID;
        else { // See Hackers Delight, sec 3.2, where n < (1 << 16)/*Hackers Delight,一本基于计算机体系结构讲解算法实现的书*/
            n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8;/*逻辑右移,左边补0,算术右移补符号位,右边均截掉*/
        }
        workers = new ForkJoinWorkerThread[n + 1]; /*工作线程数组*/
        this.submissionLock = new ReentrantLock(); /*任务队列的锁,并且是条件锁,用于添加任务时进行同步*/
        this.termination = submissionLock.newCondition();
        StringBuilder sb = new StringBuilder("ForkJoinPool-");
        sb.append(poolNumberGenerator.incrementAndGet());
        sb.append("-worker-");
        this.workerNamePrefix = sb.toString();
    }


/*ForkJoinWorkerThread 数组,如何与ForkJoinTask关联?*/
ForkJoinPool的invoke方法,调用 addSubmission 方法
【将待执行的任务,加入到任务队列】
   private void addSubmission(ForkJoinTask<?> t) 
{
final ReentrantLock lock = this.submissionLock;    /*get lock created in constructor*/
lock.lock();
try {
ForkJoinTask<?>[] q; 
int s, m;
if ((q = submissionQueue) != null) /*check if task queue is not null*/
{    // ignore if queue removed
long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;  /*不知道计算什么*/
UNSAFE.putOrderedObject(q, u, t);
/*使用了 sun.misc.Unsafe库的接口,将指定的task添加到任务队列 submissionQueue(存储值到指定字段,但不提供可见性,如果需要具备可见性,则需要指定字段为volatile)*/
queueTop = s + 1;
if (s - queueBase == m)/*如果任务队列 submissionQueue 满了,则扩容*/
growSubmissionQueue();
}
} finally {
lock.unlock();
}
signalWork(); /*Wakes up or creates a worker*/
}

【创建worker-thread,执行任务队列的任务】
final void signalWork() {
...
addWorker();
}

private void addWorker() 
{
Throwable ex = null;
ForkJoinWorkerThread t = null;
try {
t = factory.newThread(this);/*创建一个thread对象*/
} catch (Throwable e) {
ex = e;
}
if (t == null) {  // null or exceptional factory return
long c;       // adjust counts
do {} while (!UNSAFE.compareAndSwapLong
(this, ctlOffset, c = ctl,
 (((c - AC_UNIT) & AC_MASK) |
  ((c - TC_UNIT) & TC_MASK) |
  (c & ~(AC_MASK|TC_MASK)))));
// Propagate exception if originating from an external caller
if (!tryTerminate(false) && ex != null &&
!(Thread.currentThread() instanceof ForkJoinWorkerThread))
UNSAFE.throwException(ex);
}
else
t.start(); /*启动线程*/
}

【ForkJoinWorkerThread 的执行函数】
public void run() 
{
Throwable exception = null;
try {
onStart();
pool.work(this);/*执行 ForkJoinPool 对象的work方法,ForkJoinPool对象在创建worker-thread的时候指定,pool使用当前线程扫描任务队列*/
} catch (Throwable ex) {
exception = ex;
} finally {
onTermination(exception);
}
}

【worker-thread怎么执行到 submissionQueue 里的task? 】
final void work(ForkJoinWorkerThread w) 
{
boolean swept = false;                // true on empty scans
long c;
while (!w.terminate && (int)(c = ctl) >= 0) {/*检查worker-thread是否终结,*/
int a;                            // active count
if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0)
swept = scan(w, a);
/*在scan方法中,调用 ForkJoinWorkerThread.execTask(ForkJoinTask),execTask方法将调用 ForkJoinTask.doExec(),进而
调用用户实现的 compute() 方法,将结果保存在Task的result中*/
else if (tryAwaitWork(w, c))
swept = false;
}
}

【新增的worker-thread,怎样加入到ForkJoinPool的workers数组?】

/*在worker-thread的构造函数中,调用pool的 registerWorker 方法*/
protected ForkJoinWorkerThread(ForkJoinPool pool) 
{
super(pool.nextWorkerName());
this.pool = pool;
int k = pool.registerWorker(this);
...
}


【invoke如何等待compute执行完后,返回result】
ForkJoinPool的invoke方法中,最后调用 task.join()方法,等待task线程执行完毕,然后返回结果



【真实的例子】



0 0
原创粉丝点击