DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Dead Letter Queue Patterns in Apache Flink: Handling Poison Messages Without Stopping Your Stream
  • Alternative Structured Concurrency
  • From Compliance Pipes to Data Streams: Modernizing Healthcare EDI for Strategic Value
  • Building a 300 Channel Video Encoding Server

Trending

  • Designing Memory Architecture for Stateful AI Agents: Short-Term Context and Long-Term Persistence
  • Architecture Review Boards for Generative AI: A Framework for Governance, Risk Management, and Responsible Adoption
  • Part 4: Multi-Agent Orchestration — Pairing Goose With Quarkus Flow State Machines
  • MQTT, Kafka, NATS, or Zenoh? Choosing Edge-to-Cloud Messaging by Traffic Pattern

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  1. DZone
  2. Coding
  3. JavaScript
  4. A Look at ForkJoinPool and Parallel Streams

A Look at ForkJoinPool and Parallel Streams

The ForkJoin pool was put into place to support parallel streams. Get a look at how the pool works and how the work-stealing algorithm help divide up tasks.

By 
Arun Pandey
DZone Core CORE ·
Dec. 30, 16 · Tutorial
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
67.8K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

The Common ForkJoin Pool was introduced in Java 8 to support parallel streams, CompletableFuture, etc. ForkJoin is based on parallel computing, where a problem gets divided into sub-problems until the sub-problems are simple enough to solve simultaneously in separate threads, after which the results are aggregated.

The algorithm that's been used in ForkJoin implementations is the 'Work-Stealing Algorithm' to ensure that no CPU is idle.

Let's take a look at work-stealing algorithm.

Suppose there are four processors on a system and Task T has been divided into 12 subtasks, shown below, from T1 to T12.

Each processor has three subtasks. But suppose Processor 3 is too busy with work and Processor 2 is idle—it either finished all the tasks or it's in a waiting state. Then, Processor 2 will ask to Processor 3 if it needs help and takes a percentage of the tasks. So, Processor 2 is 'stealing the work' from Processor 3.

The below snapshot depicts this process:

Image title


Now let's look at the snapshot below to understand the ForkJoin framework:

Image title

So above snapshot shows the recursive approach to divide the task until subtask is a simple task.

We create an instance of ForkJoinPool as below -

 ForkJoinPool pool = new ForkJoinPool(parallelism);

Here, parallelism is the target parallelism level (number of processors), which can be seen below:

 Runtime.getRuntime().availableProcessors(); // returns 4 on my system 

To support parallelism for collections using parallel streams, a common ForkJoinPool is used internally.

We can get a common pool using the ForkJoin static method below:

 ForkJoinPool commonPool = ForkJoinPool.commonPool();

 To see the parallelism level for this commonPool, use:

commonPool.getParallelism(); //returns 3 on my system

The ForkJoin framework provides two types of tasks: 'RecursiveTask' and 'RecursiveAction'.  The difference between these two is that RecursiveTask can return a value while RecursiveAction cannot.

Let's see an example using RecursiveTask.

FibonacciComutation.java:

package arun.test.forkjoin;

import java.util.concurrent.RecursiveTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author arun.pandey
 */
public class FibonacciComputation extends RecursiveTask<Integer> {
    public static final Log LOG = LogFactory.getLog(ForkJoinTest.class);
    private final int number;

    public FibonacciComputation(int number) { 
        this.number = number; 
    }

    @Override
    public Integer compute() {
        if (number <= 1)
        return number;
        FibonacciComputation f1 = new FibonacciComputation(number - 1);
        f1.fork();
        LOG.info("Current Therad Name = "+Thread.currentThread().getName());
        FibonacciComputation f2 = new FibonacciComputation(number - 2);
        return f2.compute() + (Integer)f1.join();
    }
}


The client code is shown below:

FibonacciComutationClient.java:

package arun.test.forkjoin;

import java.util.concurrent.ForkJoinPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

 /**
  * @author arun.pandey
  */
public class FibonacciComutationClient {
    public static final Log LOG = LogFactory.getLog(FibonacciComutationClient.class);

    public static void main(String args[]){
        //to calculate 20th element of Fibonacci-Series
        int number = 20; 
        int poolSize = Runtime.getRuntime().availableProcessors();
        ForkJoinPool pool = new ForkJoinPool(poolSize);   
        long beforeTime = System.currentTimeMillis();
        LOG.info("Parallelism  => "+ pool.getParallelism());

        Integer result = (Integer) pool.invoke(new FibonacciComputation(number)); 

        LOG.info("Total Time in MilliSecond Taken ->  "+ (System.currentTimeMillis() - beforeTime));
        LOG.info(number +"the element of Fibonacci Number = "+result);
    }
}


If you execute the client code, you can see the execution time lesser than a normal recursive Fibonacci series implementation. Since it's dividing into subtasks it's a bit faster.

I hope this gives you a better understanding of the ForkJoin framework. Happy learning!

Stream (computing)

Opinions expressed by DZone contributors are their own.

Related

  • Dead Letter Queue Patterns in Apache Flink: Handling Poison Messages Without Stopping Your Stream
  • Alternative Structured Concurrency
  • From Compliance Pipes to Data Streams: Modernizing Healthcare EDI for Strategic Value
  • Building a 300 Channel Video Encoding Server

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook