ForkJoinPool invoke、execute和submit區別

使用ForkJoinPool的時候發現執行任務的方法有:

  • invoke(ForkJoinTask task)
  • execute(ForkJoinTask<?> task)
  • submit(ForkJoinTask task)

三種方式,現在總結下區別。
首先比較execute和submit的區別,觀察源碼發現:

    /**
     * Arranges for (asynchronous) execution of the given task.
     *
     * @param task the task
     * @throws NullPointerException if the task is null
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     */
    public void execute(ForkJoinTask<?> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
    }
    /**
     * Submits a ForkJoinTask for execution.
     *
     * @param task the task to submit
     * @param <T> the type of the task's result
     * @return the task
     * @throws NullPointerException if the task is null
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     */
    public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task;
    }

從方法體和返回參數可以看出,兩者邏輯大致相同,都是首先對任務做非空校驗,再將任務壓入執行隊列,唯一的不同是submit會把任務對象本身返回,返回後我們可以通過get()獲取方法執行結果。

再看invoke()

    /**
     * Performs the given task, returning its result upon completion.
     * If the computation encounters an unchecked Exception or Error,
     * it is rethrown as the outcome of this invocation.  Rethrown
     * exceptions behave in the same way as regular exceptions, but,
     * when possible, contain stack traces (as displayed for example
     * using {@code ex.printStackTrace()}) of both the current thread
     * as well as the thread actually encountering the exception;
     * minimally only the latter.
     *
     * @param task the task
     * @param <T> the type of the task's result
     * @return the task's result
     * @throws NullPointerException if the task is null
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     */
    public <T> T invoke(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task.join();
    }

submit()不同的是返回task.join(),再看join()方法

    /**
     * Returns the result of the computation when it {@link #isDone is
     * done}.  This method differs from {@link #get()} in that
     * abnormal completion results in {@code RuntimeException} or
     * {@code Error}, not {@code ExecutionException}, and that
     * interrupts of the calling thread do <em>not</em> cause the
     * method to abruptly return by throwing {@code
     * InterruptedException}.
     *
     * @return the computed result
     */
    public final V join() {
        int s;
        if ((s = doJoin() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }

首先判斷任務是否執行完畢,若未執行完畢,拋出任務取消或者任務異常的異常Exception,否則獲取任務執行結果。而getRawResult()就是取任務的結果返回。這裏看判斷任務執行完畢的doJoin()方法

    /**
     * Implementation for join, get, quietlyJoin. Directly handles
     * only cases of already-completed, external wait, and
     * unfork+exec.  Others are relayed to ForkJoinPool.awaitJoin.
     *
     * @return status upon completion
     */
    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();
    }

這裏用了多個三元運算符,整理下邏輯

  • 判斷當前任務的執行狀態
    • 當前狀態不是已完成
      • 當前線程是ForkJoin線程
        • 從工作隊列中取出一個任務執行
      • 否則等待當前任務執行完畢

所以invoke()方法的調用,會一直阻塞到任務執行完成返回

總結一下:

  1. execute(ForkJoinTask) 異步執行tasks,無返回值
  2. invoke(ForkJoinTask) 有Join, tasks會被同步到主進程
  3. submit(ForkJoinTask) 異步執行,且帶Task返回值,可通過task.get 實現同步到主線程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章