Table of Contents
What is ForkJoin?
ForkJoinTask task
创建工人(线程)
例:ForkJoinTask实现归并排序
ForkJoin计算流程
Home Java javaTutorial Fork/Join framework and calling method in Java

Fork/Join framework and calling method in Java

May 08, 2023 am 11:10 AM
java forkjoin

    What is ForkJoin?

    ForkJoin Literally, Fork means bifurcation, and Join means combination. We can understand it as combining large tasks Split into small tasks for calculation and solution, and finally combine the results of the small tasks to find the solution to the large task. We can hand over these fissioned small tasks to different threads for calculation, which is the principle of distributed computing. A thought. This is similar to distributed offline computing MapReduce in big data. One of the most classic applications of ForkJoin is Stream in Java8. We know that Stream is divided into serial stream and parallel stream. The parallel stream parallelStream relies on ForkJoin to achieve parallel processing. of.

    Let’s take a look at the core ForkJoinTask and ForkJoinPool.

    ForkJoinTask task

    The dependencies of ForkJoinTask itself are not complicated. It implements the Future interface just like the asynchronous task calculation FutureTask

    Fork/Join framework and calling method in Java

    Let's study the core source code of ForkJoinTask and how the task is calculated through the divide and conquer method.

    The core of ForkJoinTask is the fork() and join() methods.

    fork()

    • Determine whether the current thread is a ForkJoinWorkerThread thread

      • Yes Directly push the current thread into the work queue

      • Whether to call the externalPush method of ForkJoinPool

    ##In

    ForkJoinPool A static common object is constructed, and what is called here is commonexternalPush()

    #join()

    • Call the doJoin() method and wait for the thread execution to complete

    •     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;
          }
      
          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();
          }
      
      	// 获取结果的方法由子类实现
      	public abstract V getRawResult();	
      Copy after login
    RecursiveTask is a subclass of ForkJoinTask that mainly implements the method of obtaining results through generic constraints result. If we need to create a task ourselves, we still need to implement RecursiveTask and write the core calculation method compute().

    public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
        private static final long serialVersionUID = 5232453952276485270L;
    
        V result;
    
        protected abstract V compute();
    
        public final V getRawResult() {
            return result;
        }
    
        protected final void setRawResult(V value) {
            result = value;
        }
        protected final boolean exec() {
            result = compute();
            return true;
        }
    
    }
    Copy after login

    ForkJoinPool thread pool

    Many functions in ForkJoinTask rely on the ForkJoinPool thread pool, so ForkJoinTask cannot run without ForkJoinPool. ForkJoinPool has many similarities with ThreadPoolExecutor. It is specially used for The thread pool that executes the ForkJoinTask task. I have previously introduced the thread pool technology in an article. If you are interested, you can read it - analyze the implementation principle of the thread pool (pooling technology) from the java source code

    ForkJoinPool The inheritance relationship with ThreadPoolExecutor is almost the same, they are equivalent to brothers.

    Fork/Join framework and calling method in Java

    Work stealing algorithm

    The work stealing algorithm is adopted in ForkJoinPool. If a new thread is created for each fork subtask, the system resources will be affected. The overhead is huge, so a thread pool must be used. A general thread pool has only one task queue, but for ForkJoinPool, since the subtasks forked out by the same task are parallel, in order to improve efficiency and reduce thread competition, these parallel tasks need to be placed in different queues. , since threads process different tasks at different speeds, there may be a thread that has finished executing the tasks in its own queue first. At this time, in order to improve efficiency, the thread can be allowed to "

    steal" other task queues This is the so-called "work-stealing algorithm".

    For general queues, the elements entering the queue are at the end of the queue, and the elements leaving the queue are at the beginning. To meet the needs of "work stealing", the task queue should support dequeuing elements from the "tail" , which can reduce conflicts with other worker threads (because other worker threads will get the tasks in their own task queues from the head of the team). In this case, a double-ended blocking queue needs to be used to solve the problem.

    Construction method

    First let’s look at the construction method of the ForkJoinPool thread pool. It provides us with three forms of construction, the most complex of which is the construction of four input parameters. Next we Take a look at what its four input parameters represent?

    • int parallelism parallelism level (does not represent the maximum number of existing threads)

    • ForkJoinWorkerThreadFactory factory Thread creation factory

    • UncaughtExceptionHandler handler Exception capture handler

    • boolean asyncMode first-in-first-out working mode or last-in-first-out working mode

    •     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();
          }
      Copy after login
    Submission method

    Let’s take a look at the method of submitting a task:

    externalPushThis method is very familiar to us, it is exactly when forking If the current thread is not ForkJoinWorkerThread, the newly submitted task will also be executed through this method. It can be seen that fork is to create a new subtask for submission.

    externalSubmit是最为核心的一个方法,它可以首次向池提交第一个任务,并执行二次初始化。它还可以检测外部线程的首次提交,并创建一个新的共享队列。

    signalWork(ws, q)是发送工作信号,让工作队列进行运转。

        public ForkJoinTask<?> submit(Runnable task) {
            if (task == null)
                throw new NullPointerException();
            ForkJoinTask<?> job;
            if (task instanceof ForkJoinTask<?>) // avoid re-wrap
                job = (ForkJoinTask<?>) task;
            else
                job = new ForkJoinTask.AdaptedRunnableAction(task);
            externalPush(job);
            return job;
        }
    
        final void externalPush(ForkJoinTask<?> task) {
            WorkQueue[] ws; WorkQueue q; int m;
            int r = ThreadLocalRandom.getProbe();
            int rs = runState;
            if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
                (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
                U.compareAndSwapInt(q, QLOCK, 0, 1)) {
                ForkJoinTask<?>[] a; int am, n, s;
                if ((a = q.array) != null &&
                    (am = a.length - 1) > (n = (s = q.top) - q.base)) {
                    int j = ((am & s) << ASHIFT) + ABASE;
                    U.putOrderedObject(a, j, task);
                    U.putOrderedInt(q, QTOP, s + 1);
                    U.putOrderedInt(q, QLOCK, 0);
                    if (n <= 1)
                        signalWork(ws, q);
                    return;
                }
                U.compareAndSwapInt(q, QLOCK, 1, 0);
            }
            externalSubmit(task);
        }
    
        private void externalSubmit(ForkJoinTask<?> task) {
            int r;                                    // initialize caller&#39;s probe
            if ((r = ThreadLocalRandom.getProbe()) == 0) {
                ThreadLocalRandom.localInit();
                r = ThreadLocalRandom.getProbe();
            }
            for (;;) {
                WorkQueue[] ws; WorkQueue q; int rs, m, k;
                boolean move = false;
                if ((rs = runState) < 0) {
                    tryTerminate(false, false);     // help terminate
                    throw new RejectedExecutionException();
                }
                else if ((rs & STARTED) == 0 ||     // initialize
                         ((ws = workQueues) == null || (m = ws.length - 1) < 0)) {
                    int ns = 0;
                    rs = lockRunState();
                    try {
                        if ((rs & STARTED) == 0) {
                            U.compareAndSwapObject(this, STEALCOUNTER, null,
                                                   new AtomicLong());
                            // create workQueues array with size a power of two
                            int p = config & SMASK; // ensure at least 2 slots
                            int n = (p > 1) ? p - 1 : 1;
                            n |= n >>> 1; n |= n >>> 2;  n |= n >>> 4;
                            n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
                            workQueues = new WorkQueue[n];
                            ns = STARTED;
                        }
                    } finally {
                        unlockRunState(rs, (rs & ~RSLOCK) | ns);
                    }
                }
                else if ((q = ws[k = r & m & SQMASK]) != null) {
                    if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
                        ForkJoinTask<?>[] a = q.array;
                        int s = q.top;
                        boolean submitted = false; // initial submission or resizing
                        try {                      // locked version of push
                            if ((a != null && a.length > s + 1 - q.base) ||
                                (a = q.growArray()) != null) {
                                int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
                                U.putOrderedObject(a, j, task);
                                U.putOrderedInt(q, QTOP, s + 1);
                                submitted = true;
                            }
                        } finally {
                            U.compareAndSwapInt(q, QLOCK, 1, 0);
                        }
                        if (submitted) {
                            signalWork(ws, q);
                            return;
                        }
                    }
                    move = true;                   // move on failure
                }
                else if (((rs = runState) & RSLOCK) == 0) { // create new queue
                    q = new WorkQueue(this, null);
                    q.hint = r;
                    q.config = k | SHARED_QUEUE;
                    q.scanState = INACTIVE;
                    rs = lockRunState();           // publish index
                    if (rs > 0 &&  (ws = workQueues) != null &&
                        k < ws.length && ws[k] == null)
                        ws[k] = q;                 // else terminated
                    unlockRunState(rs, rs & ~RSLOCK);
                }
                else
                    move = true;                   // move if busy
                if (move)
                    r = ThreadLocalRandom.advanceProbe(r);
            }
        }
    Copy after login

    创建工人(线程)

    提交任务后,通过signalWork(ws, q)方法,发送工作信号,当符合没有执行完毕,且没有出现异常的条件下,循环执行任务,根据控制变量尝试添加工人(线程),通过线程工厂,生成线程,并且启动线程,也控制着工人(线程)的下岗。

        final void signalWork(WorkQueue[] ws, WorkQueue q) {
            long c; int sp, i; WorkQueue v; Thread p;
            while ((c = ctl) < 0L) {                       // too few active
                if ((sp = (int)c) == 0) {                  // no idle workers
                    if ((c & ADD_WORKER) != 0L)            // too few workers
                        tryAddWorker(c);
                    break;
                }
                if (ws == null)                            // unstarted/terminated
                    break;
                if (ws.length <= (i = sp & SMASK))         // terminated
                    break;
                if ((v = ws[i]) == null)                   // terminating
                    break;
                int vs = (sp + SS_SEQ) & ~INACTIVE;        // next scanState
                int d = sp - v.scanState;                  // screen CAS
                long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred);
                if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {
                    v.scanState = vs;                      // activate v
                    if ((p = v.parker) != null)
                        U.unpark(p);
                    break;
                }
                if (q != null && q.base == q.top)          // no more work
                    break;
            }
        }
    
        private void tryAddWorker(long c) {
            boolean add = false;
            do {
                long nc = ((AC_MASK & (c + AC_UNIT)) |
                           (TC_MASK & (c + TC_UNIT)));
                if (ctl == c) {
                    int rs, stop;                 // check if terminating
                    if ((stop = (rs = lockRunState()) & STOP) == 0)
                        add = U.compareAndSwapLong(this, CTL, c, nc);
                    unlockRunState(rs, rs & ~RSLOCK);
                    if (stop != 0)
                        break;
                    if (add) {
                        createWorker();
                        break;
                    }
                }
            } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);
        }
    
        private boolean createWorker() {
            ForkJoinWorkerThreadFactory fac = factory;
            Throwable ex = null;
            ForkJoinWorkerThread wt = null;
            try {
                if (fac != null && (wt = fac.newThread(this)) != null) {
                    wt.start();
                    return true;
                }
            } catch (Throwable rex) {
                ex = rex;
            }
            deregisterWorker(wt, ex);
            return false;
        }
    
       final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
            WorkQueue w = null;
            if (wt != null && (w = wt.workQueue) != null) {
                WorkQueue[] ws;                           // remove index from array
                int idx = w.config & SMASK;
                int rs = lockRunState();
                if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w)
                    ws[idx] = null;
                unlockRunState(rs, rs & ~RSLOCK);
            }
            long c;                                       // decrement counts
            do {} while (!U.compareAndSwapLong
                         (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |
                                               (TC_MASK & (c - TC_UNIT)) |
                                               (SP_MASK & c))));
            if (w != null) {
                w.qlock = -1;                             // ensure set
                w.transferStealCount(this);
                w.cancelAll();                            // cancel remaining tasks
            }
            for (;;) {                                    // possibly replace
                WorkQueue[] ws; int m, sp;
                if (tryTerminate(false, false) || w == null || w.array == null ||
                    (runState & STOP) != 0 || (ws = workQueues) == null ||
                    (m = ws.length - 1) < 0)              // already terminating
                    break;
                if ((sp = (int)(c = ctl)) != 0) {         // wake up replacement
                    if (tryRelease(c, ws[sp & m], AC_UNIT))
                        break;
                }
                else if (ex != null && (c & ADD_WORKER) != 0L) {
                    tryAddWorker(c);                      // create replacement
                    break;
                }
                else                                      // don&#39;t need replacement
                    break;
            }
            if (ex == null)                               // help clean on way out
                ForkJoinTask.helpExpungeStaleExceptions();
            else                                          // rethrow
                ForkJoinTask.rethrow(ex);
        }
    
        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);
            }
        }
        protected ForkJoinWorkerThread(ForkJoinPool pool) {
            // Use a placeholder until a useful name can be set in registerWorker
            super("aForkJoinWorkerThread");
            this.pool = pool;
            this.workQueue = pool.registerWorker(this);
        }
    
        final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
            UncaughtExceptionHandler handler;
            wt.setDaemon(true);                           // configure thread
            if ((handler = ueh) != null)
                wt.setUncaughtExceptionHandler(handler);
            WorkQueue w = new WorkQueue(this, wt);
            int i = 0;                                    // assign a pool index
            int mode = config & MODE_MASK;
            int rs = lockRunState();
            try {
                WorkQueue[] ws; int n;                    // skip if no array
                if ((ws = workQueues) != null && (n = ws.length) > 0) {
                    int s = indexSeed += SEED_INCREMENT;  // unlikely to collide
                    int m = n - 1;
                    i = ((s << 1) | 1) & m;               // odd-numbered indices
                    if (ws[i] != null) {                  // collision
                        int probes = 0;                   // step by approx half n
                        int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
                        while (ws[i = (i + step) & m] != null) {
                            if (++probes >= n) {
                                workQueues = ws = Arrays.copyOf(ws, n <<= 1);
                                m = n - 1;
                                probes = 0;
                            }
                        }
                    }
                    w.hint = s;                           // use as random seed
                    w.config = i | mode;
                    w.scanState = i;                      // publication fence
                    ws[i] = w;
                }
            } finally {
                unlockRunState(rs, rs & ~RSLOCK);
            }
            wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
            return w;
        }
    Copy after login

    例:ForkJoinTask实现归并排序

    这里我们就用经典的归并排序为例,构建一个我们自己的ForkJoinTask,按照归并排序的思路,重写其核心的compute()方法,通过ForkJoinPool.submit(task)提交任务,通过get()同步获取任务执行结果。

    package com.zhj.interview;
    
    import java.util.*;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.RecursiveTask;
    
    public class Test16 {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            int[] bigArr = new int[10000000];
            for (int i = 0; i < 10000000; i++) {
                bigArr[i] = (int) (Math.random() * 10000000);
            }
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            MyForkJoinTask task = new MyForkJoinTask(bigArr);
            long start = System.currentTimeMillis();
            forkJoinPool.submit(task).get();
            long end = System.currentTimeMillis();
            System.out.println("耗时:" + (end-start));
    	}
    
    }
    class MyForkJoinTask extends RecursiveTask<int[]> {
    
        private int source[];
    
        public MyForkJoinTask(int source[]) {
            if (source == null) {
                throw new RuntimeException("参数有误!!!");
            }
            this.source = source;
        }
    
        @Override
        protected int[] compute() {
            int l = source.length;
            if (l < 2) {
                return Arrays.copyOf(source, l);
            }
            if (l == 2) {
                if (source[0] > source[1]) {
                    int[] tar = new int[2];
                    tar[0] = source[1];
                    tar[1] = source[0];
                    return tar;
                } else {
                    return Arrays.copyOf(source, l);
                }
            }
            if (l > 2) {
                int mid = l / 2;
                MyForkJoinTask task1 = new MyForkJoinTask(Arrays.copyOf(source, mid));
                task1.fork();
                MyForkJoinTask task2 = new MyForkJoinTask(Arrays.copyOfRange(source, mid, l));
                task2.fork();
                int[] res1 = task1.join();
                int[] res2 = task2.join();
                int tar[] = merge(res1, res2);
                return tar;
            }
            return null;
        }
    	// 合并数组
        private int[] merge(int[] res1, int[] res2) {
            int l1 = res1.length;
            int l2 = res2.length;
            int l = l1 + l2;
            int tar[] = new int[l];
            for (int i = 0, i1 = 0, i2 = 0; i < l; i++) {
                int v1 = i1 >= l1 ? Integer.MAX_VALUE : res1[i1];
                int v2 = i2 >= l2 ? Integer.MAX_VALUE : res2[i2];
                // 如果条件成立,说明应该取数组array1中的值
                if(v1 < v2) {
                    tar[i] = v1;
                    i1++;
                } else {
                    tar[i] = v2;
                    i2++;
                }
            }
            return tar;
        }
    }
    Copy after login

    ForkJoin计算流程

    通过ForkJoinPool提交任务,获取结果流程如下,拆分子任务不一定是二分的形式,可参照MapReduce的模式,也可以按照具体需求进行灵活的设计。

    Fork/Join framework and calling method in Java

    The above is the detailed content of Fork/Join framework and calling method in Java. For more information, please follow other related articles on the PHP Chinese website!

    Statement of this Website
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

    Hot AI Tools

    Undresser.AI Undress

    Undresser.AI Undress

    AI-powered app for creating realistic nude photos

    AI Clothes Remover

    AI Clothes Remover

    Online AI tool for removing clothes from photos.

    Undress AI Tool

    Undress AI Tool

    Undress images for free

    Clothoff.io

    Clothoff.io

    AI clothes remover

    AI Hentai Generator

    AI Hentai Generator

    Generate AI Hentai for free.

    Hot Article

    R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
    2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
    Repo: How To Revive Teammates
    4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
    Hello Kitty Island Adventure: How To Get Giant Seeds
    4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

    Hot Tools

    Notepad++7.3.1

    Notepad++7.3.1

    Easy-to-use and free code editor

    SublimeText3 Chinese version

    SublimeText3 Chinese version

    Chinese version, very easy to use

    Zend Studio 13.0.1

    Zend Studio 13.0.1

    Powerful PHP integrated development environment

    Dreamweaver CS6

    Dreamweaver CS6

    Visual web development tools

    SublimeText3 Mac version

    SublimeText3 Mac version

    God-level code editing software (SublimeText3)

    Square Root in Java Square Root in Java Aug 30, 2024 pm 04:26 PM

    Guide to Square Root in Java. Here we discuss how Square Root works in Java with example and its code implementation respectively.

    Perfect Number in Java Perfect Number in Java Aug 30, 2024 pm 04:28 PM

    Guide to Perfect Number in Java. Here we discuss the Definition, How to check Perfect number in Java?, examples with code implementation.

    Random Number Generator in Java Random Number Generator in Java Aug 30, 2024 pm 04:27 PM

    Guide to Random Number Generator in Java. Here we discuss Functions in Java with examples and two different Generators with ther examples.

    Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

    Guide to Weka in Java. Here we discuss the Introduction, how to use weka java, the type of platform, and advantages with examples.

    Armstrong Number in Java Armstrong Number in Java Aug 30, 2024 pm 04:26 PM

    Guide to the Armstrong Number in Java. Here we discuss an introduction to Armstrong's number in java along with some of the code.

    Smith Number in Java Smith Number in Java Aug 30, 2024 pm 04:28 PM

    Guide to Smith Number in Java. Here we discuss the Definition, How to check smith number in Java? example with code implementation.

    Java Spring Interview Questions Java Spring Interview Questions Aug 30, 2024 pm 04:29 PM

    In this article, we have kept the most asked Java Spring Interview Questions with their detailed answers. So that you can crack the interview.

    Break or return from Java 8 stream forEach? Break or return from Java 8 stream forEach? Feb 07, 2025 pm 12:09 PM

    Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

    See all articles