Basic Question about F/J

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Basic Question about F/J

Dr Heinz M. Kabutz
Hi everyone,

inside the compute method of a recursive action or task, I usually
recommend calling the invoke() method of any created FJTask.  However,
in the RecursiveTask example, the compute() method of f2 is called.  
Putting aside the fact that it's a dumb algorithm and that no, you would
not want to use the linear algorithm either, but instead a recursive
decomposition version of Dijkstra's Sum of Squares, I was just wondering
whether it is more correct to call compute() or invoke() on a task that
you wish to call on your current thread?

Regards

Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

David Holmes-6
Hi Heinz,

Dr Heinz M. Kabutz writes:

> Hi everyone,
>
> inside the compute method of a recursive action or task, I usually
> recommend calling the invoke() method of any created FJTask.  However,
> in the RecursiveTask example, the compute() method of f2 is called.
> Putting aside the fact that it's a dumb algorithm and that no, you would
> not want to use the linear algorithm either, but instead a recursive
> decomposition version of Dijkstra's Sum of Squares, I was just wondering
> whether it is more correct to call compute() or invoke() on a task that
> you wish to call on your current thread?

invoke() doesn't process the task on the current thread - it arranges for
async execution and then waits for it to complete - so you are wasting a
thread if you use invoke() rather than compute().

The optimal version of a compute() method would do:

  rs.fork();
  ls.compute();
  if (rs.tryUnfork())
    rs.compute();
  else
    rs.join();

Ref: "Data parallelism with the ForkJoin Framework in Java 7" - JavaOne SF
2011

Cheers,
David

> Regards
>
> Heinz
> --
> Dr Heinz M. Kabutz (PhD CompSci)
> Author of "The Java(tm) Specialists' Newsletter"
> Sun/Oracle Java Champion since 2005
> JavaOne Rock Star Speaker 2012
> http://www.javaspecialists.eu
> Tel: +30 69 75 595 262
> Skype: kabutz
>
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

Aleksey Shipilev-2
In reply to this post by Dr Heinz M. Kabutz
On 06/02/2015 08:08 PM, Dr Heinz M. Kabutz wrote:
> inside the compute method of a recursive action or task, I usually
> recommend calling the invoke() method of any created FJTask.  However,
> in the RecursiveTask example, the compute() method of f2 is called.
> Putting aside the fact that it's a dumb algorithm and that no, you would
> not want to use the linear algorithm either, but instead a recursive
> decomposition version of Dijkstra's Sum of Squares, I was just wondering
> whether it is more correct to call compute() or invoke() on a task that
> you wish to call on your current thread?

Correctness is in the eye of beholder here.

compute() does not call through any FJP infrastructure
(RecursiveTask.compute() is abstract), it just calls the payload method.
Think about compute() as the naked payload method.

invoke() calls through FJP code, and among other things, it will compute
and store the result or return immediately if the result is available
already, plus notify waiters the result is available (notice
RecursiveTask<T> is also a Future<T>). But in the end, it will try to
call compute() in the same thread:

 * ... Method {@link #invoke} is semantically
 * equivalent to {@code fork(); join()} but always attempts to begin
 * execution in the current thread. ...

AFAIU, current implementation does not even try to fork, and goes
straight to execution.

Since two compute()-s will execute the payload method twice, and
invoke() will behave like one-shot action (which is what you probably
want anyway when you are decomposing stuff), I'd recommend using
invoke() as a rule to avoid double computations and unblock whoever is
waiting on task result.

If your code is trivial, and there is no room for a programming error,
you may save a few cycles going straight to compute(). (Or,
understanding this is just a plain payload call, rewrite the recursion
into the loop).

I think RecursiveTask Fibonacci example is written that particular way
to demonstrate how would you "fork off" an right-hand-side execution,
while still doing an old-style recursive call on the left-hand-side.
That is, not to mix the invoke() in this story unnecessarily.

-Aleksey.


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

Aleksey Shipilev-2
In reply to this post by David Holmes-6
On 06/02/2015 11:53 PM, David Holmes wrote:
> invoke() doesn't process the task on the current thread - it arranges for
> async execution and then waits for it to complete - so you are wasting a
> thread if you use invoke() rather than compute().

Current implementation tries to process invoke() in the same thread. FJP
is actually about not wasting threads on dumb stuff :)

> The optimal version of a compute() method would do:
>
>   rs.fork();
>   ls.compute();
>   if (rs.tryUnfork())
>     rs.compute();
>   else
>     rs.join();
>
> Ref: "Data parallelism with the ForkJoin Framework in Java 7" - JavaOne SF
> 2011
I don't think it is necessary to expose these low-level things anymore.
FJT.join() does the same tryUnpush and exec undercover, so:

   rs.fork();
   T lres = ls.compute();
   T rres = rs.join();
   return combine(lres, rres);

...is enough. Or, if you don't care about results, just

   ForkJoinTask.invokeAll(ls, rs);

-Aleksey


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

David Holmes-6
Aleksey Shipilev writes:
> On 06/02/2015 11:53 PM, David Holmes wrote:
> > invoke() doesn't process the task on the current thread - it
> arranges for
> > async execution and then waits for it to complete - so you are wasting a
> > thread if you use invoke() rather than compute().
>
> Current implementation tries to process invoke() in the same thread. FJP
> is actually about not wasting threads on dumb stuff :)

Yes it tries, so I should have said "potentially wasting a thread".

Cheers,
David

> > The optimal version of a compute() method would do:
> >
> >   rs.fork();
> >   ls.compute();
> >   if (rs.tryUnfork())
> >     rs.compute();
> >   else
> >     rs.join();
> >
> > Ref: "Data parallelism with the ForkJoin Framework in Java 7" -
> JavaOne SF
> > 2011
>
> I don't think it is necessary to expose these low-level things anymore.
> FJT.join() does the same tryUnpush and exec undercover, so:
>
>    rs.fork();
>    T lres = ls.compute();
>    T rres = rs.join();
>    return combine(lres, rres);
>
> ...is enough. Or, if you don't care about results, just
>
>    ForkJoinTask.invokeAll(ls, rs);
>
> -Aleksey
>
>

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

Peter Levart
In reply to this post by Dr Heinz M. Kabutz
Hi,

On 06/02/2015 07:08 PM, Dr Heinz M. Kabutz wrote:

> Hi everyone,
>
> inside the compute method of a recursive action or task, I usually
> recommend calling the invoke() method of any created FJTask. However,
> in the RecursiveTask example, the compute() method of f2 is called.  
> Putting aside the fact that it's a dumb algorithm and that no, you
> would not want to use the linear algorithm either, but instead a
> recursive decomposition version of Dijkstra's Sum of Squares, I was
> just wondering whether it is more correct to call compute() or
> invoke() on a task that you wish to call on your current thread?
>
> Regards
>
> Heinz

I think David and Aleksey have already answered your question pretty
well. I don't think RecursiveTask.compute() method is meant to be called
directly. It is a protected abstract method after all that is meant to
be implemented by subclass and called by FJ-pool. It may be OK to call
it for demonstration purposes, but otherwise it is pointless to pack the
parameters of a computation into an object meant to execute in a managed
environment just to invoke it's implemented method directly. Better use
of resources can be achieved by factoring the computation into a static
method that takes all the parameters (can be hosted in the FJ task
itself) and just call that method with the parameters instead of
creating the task with them and calling compute(). The FJ-task can
implement it's compute() method by delegating to the same static method
then.

Regards, Peter

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

thurstonn
In reply to this post by Dr Heinz M. Kabutz
Dr Heinz M. Kabutz wrote
Hi everyone,

inside the compute method of a recursive action or task, I usually
recommend calling the invoke() method of any created FJTask.  
Do you mean that your implementations do:

<code>
class MyRecursiveTask

Integer compute()
MyRecursiveTask right = new MyRecursiveTask(...)
MyRecursiveTask left = new MyRecursiveTask(...)
return right.invoke() + left.invoke()
</code>

Surely not?
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

Dr Heinz M. Kabutz
LOL, no of course not.


It would actually be nice to have an invokeAll() method also for RecursiveTask, not just RecursiveAction.  To do that, we would need a merge function that can combine results.  Something like this ....

    public static <T> T invokeAll(BiFunction<T, T, T> merger,
                                  T initial,
                                  RecursiveTask<T>... tasks) {
        T result = initial;
        Throwable ex = null;
        int last = tasks.length - 1;
        for (int i = last; i >= 0; --i) {
            ForkJoinTask<T> t = tasks[i];
            if (t == null) {
                if (ex == null)
                    ex = new NullPointerException();
            } else if (i != 0)
                t.fork();
            else {
                result = merger.apply(result, t.invoke());
                if (ex == null && t.isCompletedAbnormally()) {
                    ex = t.getException();
                }
            }
        }
        for (int i = 1; i <= last; ++i) {
            ForkJoinTask<T> t = tasks[i];
            if (t != null) {
                if (ex != null)
                    t.cancel(false);
                else {
                    result = merger.apply(result, t.join());                   
                    if (t.isCompletedAbnormally()) {
                        ex = t.getException();
                    }
                }
            }
        }
        if (ex != null)
            rethrow(ex);
        return result;
    }


Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz


thurstonn wrote:
Dr Heinz M. Kabutz wrote
  
Hi everyone,

inside the compute method of a recursive action or task, I usually 
recommend calling the invoke() method of 
    
*
  
any created FJTask
    
*
  
.  
    

Do you mean that your implementations do:

<code>
class MyRecursiveTask

Integer compute()
MyRecursiveTask right = new MyRecursiveTask(...)
MyRecursiveTask left = new MyRecursiveTask(...)
return right.invoke() + left.invoke()
</code>

Surely not?




--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/Basic-Question-about-F-J-tp12595p12605.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

  

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

oleksandr otenko
Wouldn't it be better to have fold in Arrays (or reduce like in Stream, but specialized to arrays)

Alex

On 12/06/2015 13:04, Dr Heinz M. Kabutz wrote:
LOL, no of course not.


It would actually be nice to have an invokeAll() method also for RecursiveTask, not just RecursiveAction.  To do that, we would need a merge function that can combine results.  Something like this ....

    public static <T> T invokeAll(BiFunction<T, T, T> merger,
                                  T initial,
                                  RecursiveTask<T>... tasks) {
        T result = initial;
        Throwable ex = null;
        int last = tasks.length - 1;
        for (int i = last; i >= 0; --i) {
            ForkJoinTask<T> t = tasks[i];
            if (t == null) {
                if (ex == null)
                    ex = new NullPointerException();
            } else if (i != 0)
                t.fork();
            else {
                result = merger.apply(result, t.invoke());
                if (ex == null && t.isCompletedAbnormally()) {
                    ex = t.getException();
                }
            }
        }
        for (int i = 1; i <= last; ++i) {
            ForkJoinTask<T> t = tasks[i];
            if (t != null) {
                if (ex != null)
                    t.cancel(false);
                else {
                    result = merger.apply(result, t.join());                   
                    if (t.isCompletedAbnormally()) {
                        ex = t.getException();
                    }
                }
            }
        }
        if (ex != null)
            rethrow(ex);
        return result;
    }


Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz


thurstonn wrote:
Dr Heinz M. Kabutz wrote
  
Hi everyone,

inside the compute method of a recursive action or task, I usually 
recommend calling the invoke() method of 
    
*
  
any created FJTask
    
*
  
.  
    

Do you mean that your implementations do:

<code>
class MyRecursiveTask

Integer compute()
MyRecursiveTask right = new MyRecursiveTask(...)
MyRecursiveTask left = new MyRecursiveTask(...)
return right.invoke() + left.invoke()
</code>

Surely not?




--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/Basic-Question-about-F-J-tp12595p12605.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

  


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

Dr Heinz M. Kabutz
Thanks for the suggestion Oleksandr.

I guess we could do it that way, but it would mean that we don't have the option of canceling all the tasks if one of them throws an exception.  We would also always call fork/join, even when we could just call invoke():

    public static <T> T invokeAll(BinaryOperator<T> merger,
                                  T identity,
                                  RecursiveTask<T>... tasks) {
        Stream.of(tasks).forEach(ForkJoinTask::fork);
        return Stream.of(tasks).map(ForkJoinTask::join).reduce(identity, merger);
    }

Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz


Oleksandr Otenko wrote:
Wouldn't it be better to have fold in Arrays (or reduce like in Stream, but specialized to arrays)

Alex

On 12/06/2015 13:04, Dr Heinz M. Kabutz wrote:
LOL, no of course not.


It would actually be nice to have an invokeAll() method also for RecursiveTask, not just RecursiveAction.  To do that, we would need a merge function that can combine results.  Something like this ....

    public static <T> T invokeAll(BiFunction<T, T, T> merger,
                                  T initial,
                                  RecursiveTask<T>... tasks) {
        T result = initial;
        Throwable ex = null;
        int last = tasks.length - 1;
        for (int i = last; i >= 0; --i) {
            ForkJoinTask<T> t = tasks[i];
            if (t == null) {
                if (ex == null)
                    ex = new NullPointerException();
            } else if (i != 0)
                t.fork();
            else {
                result = merger.apply(result, t.invoke());
                if (ex == null && t.isCompletedAbnormally()) {
                    ex = t.getException();
                }
            }
        }
        for (int i = 1; i <= last; ++i) {
            ForkJoinTask<T> t = tasks[i];
            if (t != null) {
                if (ex != null)
                    t.cancel(false);
                else {
                    result = merger.apply(result, t.join());                   
                    if (t.isCompletedAbnormally()) {
                        ex = t.getException();
                    }
                }
            }
        }
        if (ex != null)
            rethrow(ex);
        return result;
    }


Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz
    


thurstonn wrote:
Dr Heinz M. Kabutz wrote
  
Hi everyone,

inside the compute method of a recursive action or task, I usually 
recommend calling the invoke() method of 
    
*
  
any created FJTask
    
*
  
.  
    

Do you mean that your implementations do:

<code>
class MyRecursiveTask

Integer compute()
MyRecursiveTask right = new MyRecursiveTask(...)
MyRecursiveTask left = new MyRecursiveTask(...)
return right.invoke() + left.invoke()
</code>

Surely not?




--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/Basic-Question-about-F-J-tp12595p12605.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

  


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
    


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

oleksandr otenko
Yes, we can cancel. There's a version of reduce that can do that.

After fork,

Stream.of(tasks).reduce(identity, (prev, next) -> {if (prev instanceof Exception) next.cancel(false);
                                                  else { T y = next.join();
                                                         if (next.isCompletedAbnormally()) prev=next.getException();
                                                         else prev = merger(prev, y);};
                                                  return prev;}, null)

You don't have to fail if one of the tasks fails - what you do upon failure is really task-dependent.

Alex

On 12/06/2015 15:01, Dr Heinz M. Kabutz wrote:
Thanks for the suggestion Oleksandr.

I guess we could do it that way, but it would mean that we don't have the option of canceling all the tasks if one of them throws an exception.  We would also always call fork/join, even when we could just call invoke():

    public static <T> T invokeAll(BinaryOperator<T> merger,
                                  T identity,
                                  RecursiveTask<T>... tasks) {
        Stream.of(tasks).forEach(ForkJoinTask::fork);
        return Stream.of(tasks).map(ForkJoinTask::join).reduce(identity, merger);
    }

Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz


Oleksandr Otenko wrote:
Wouldn't it be better to have fold in Arrays (or reduce like in Stream, but specialized to arrays)

Alex

On 12/06/2015 13:04, Dr Heinz M. Kabutz wrote:
LOL, no of course not.


It would actually be nice to have an invokeAll() method also for RecursiveTask, not just RecursiveAction.  To do that, we would need a merge function that can combine results.  Something like this ....

    public static <T> T invokeAll(BiFunction<T, T, T> merger,
                                  T initial,
                                  RecursiveTask<T>... tasks) {
        T result = initial;
        Throwable ex = null;
        int last = tasks.length - 1;
        for (int i = last; i >= 0; --i) {
            ForkJoinTask<T> t = tasks[i];
            if (t == null) {
                if (ex == null)
                    ex = new NullPointerException();
            } else if (i != 0)
                t.fork();
            else {
                result = merger.apply(result, t.invoke());
                if (ex == null && t.isCompletedAbnormally()) {
                    ex = t.getException();
                }
            }
        }
        for (int i = 1; i <= last; ++i) {
            ForkJoinTask<T> t = tasks[i];
            if (t != null) {
                if (ex != null)
                    t.cancel(false);
                else {
                    result = merger.apply(result, t.join());                   
                    if (t.isCompletedAbnormally()) {
                        ex = t.getException();
                    }
                }
            }
        }
        if (ex != null)
            rethrow(ex);
        return result;
    }


Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun/Oracle Java Champion since 2005
JavaOne Rock Star Speaker 2012
http://www.javaspecialists.eu
Tel: +30 69 75 595 262
Skype: kabutz
    


thurstonn wrote:
Dr Heinz M. Kabutz wrote
  
Hi everyone,

inside the compute method of a recursive action or task, I usually 
recommend calling the invoke() method of 
    
*
  
any created FJTask
    
*
  
.  
    

Do you mean that your implementations do:

<code>
class MyRecursiveTask

Integer compute()
MyRecursiveTask right = new MyRecursiveTask(...)
MyRecursiveTask left = new MyRecursiveTask(...)
return right.invoke() + left.invoke()
</code>

Surely not?




--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/Basic-Question-about-F-J-tp12595p12605.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

  


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
    



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

thurstonn
In reply to this post by David Holmes-6
David Holmes-6 wrote
Aleksey Shipilev writes:
> On 06/02/2015 11:53 PM, David Holmes wrote:
> > invoke() doesn't process the task on the current thread - it
> arranges for
> > async execution and then waits for it to complete - so you are wasting a
> > thread if you use invoke() rather than compute().
>
> Current implementation tries to process invoke() in the same thread. FJP
> is actually about not wasting threads on dumb stuff :)

Yes it tries, so I should have said "potentially wasting a thread".

Cheers,
David
No, invoke's logic is basically:
if (this task is not scheduled/forked)
      compute()
       
else
      join() // simplified a bit

Of course, after compute(), it does all of the necessary work for satisfying the Future contract (invoke returns void).

So in respect to the OP, I can't think of any reason to prefer invoke() over compute(), unless you intend to publish the RecursiveTask outside of the compute() method, since otherwise invoke does unnecessary writes (including a volatile write).

I've always thought of the example code in the javadoc (ignoring whether fibonacci is the best example) as idiomatic, which I think is the real point of the question
Reply | Threaded
Open this post in threaded view
|

Re: Basic Question about F/J

thurstonn
In reply to this post by Dr Heinz M. Kabutz
Well, that's what you wrote.

I guess you must have meant replacing (using the javadoc example):

Integer compute() {
      if (n <= 1)
        return n;
      Fibonacci f1 = new Fibonacci(n - 1);
      f1.fork();
      Fibonacci f2 = new Fibonacci(n - 2);
      return f2.compute() + f1.join();
    }

with:
Integer compute() {
      if (n <= 1)
        return n;
      Fibonacci f1 = new Fibonacci(n - 1);
      f1.fork();
      Fibonacci f2 = new Fibonacci(n - 2);
      return f2.invoke() + f1.join();
    }

or did you mean:
Integer compute() {
      if (n <= 1)
        return n;
      Fibonacci f1 = new Fibonacci(n - 1);
      Fibonacci f2 = new Fibonacci(n - 2);

      ForkJoinTask.invokeAll(f1, f2);
      return f1.join() + f2.join();

    }