Saturday, March 7, 2020

Fork Join pool - sometimes the solution is really simple


Let's discuss how people try to use the Fork Join pool in Java 7. Imagine that you have a class that extends RecursiveTask.


@Override
protected BigInteger compute() {
    if ((n - start) >= THRESHOLD) {
        Collection<FactorialTask> tasks = ForkJoinTask
                             .invokeAll(getSubTasks());
        BigInteger result = BigInteger.ONE;
        for (FactorialTask t : tasks) {
            result =  t.join().multiply(result);
        }
        return result;
    } else {
        return calculate(start, n);
    }

}


Ok, and do you know how some people try to transfer this code into the java 8 style? Yep, that's the code that you may find when you surf the web:

  @Override
   protected BigInteger compute() {
       if ((n - start) >= THRESHOLD) {
           return ForkJoinTask
             .invokeAll(createSubtasks())
             .stream()
             .map(ForkJoinTask::join)
             .reduce(BigInteger.ONE, BigInteger::multiply);
       } else {
           return calculate(start, n);
       }
   }


Off-course they still have additional methods:

    private BigInteger calculate(int start, int finish) {
        return IntStream.rangeClosed(start, finish)
                .mapToObj(BigInteger::valueOf)
                .reduce(BigInteger.ONE, BigInteger::multiply);

    }
    private Collection<FactorialTask> getSubTasks() {
        List<FactorialTask> tasks = new ArrayList<>();
        int mid = (start + n) / 2;
        tasks.add(new FactorialTask(start, mid));
        tasks.add(new FactorialTask(mid + 1, n));
        return tasks;
    }


In order to invoke:

public BigInteger factorial(Integer number){
    ForkJoinPool pool = ForkJoinPool.commonPool();
    return pool.invoke(new FactorialTask(number));
}

BUT wait! In Java 8+ we have a parallel stream, why don't rewrite this code in one line? Everything is  just simple:

IntStream.rangeClosed(1, number)
         .parallel()
         .mapToObj(BigInteger::valueOf)         
         .reduce(BigInteger::multiply);



Sometimes the solution is quite simple.