「ロックフリー」プログラミングを意味していると思いますhttp://en.wikipedia.org/wiki/Non-blocking_algorithm ? FutureTask.get() は現在のスレッドをブロックする可能性があります (そのため、アイドル状態の CPU が残ります) が、ForkJoinTask.get() (または結合) は CPU をビジー状態に保とうとします。
これは、問題を多くの小さなピース (ForkJoinTask) に分割できる場合にうまく機能します。1 つの FJTask が他のタスクの結果を内部的に待っている場合、それは準備ができていません。ForkJoinTask は、実行する他の作業 (タスク) をその ForkJoinPool から取得しようとし、その間にそのタスクを実行します。
すべてのタスクが CPU バウンドになるまで、問題なく動作します。すべての CPU はビジー状態のままです。タスクのいずれかが何らかの外部イベントを待機している場合 (つまり、火星探査車に REST 呼び出しを送信している場合) は機能しません。また、問題はDAGを形成する必要があります。そうしないと、デッドロックが発生する可能性があります。ただし、同じタスクで以前に分岐したタスクのみに参加するまでは、うまく機能します。最後に分岐したタスクに参加するとさらに良いでしょう。
したがって、タスク内/タスク間で get() または join() を呼び出すことはそれほど悪くありません。
問題を解決するために完了ハンドラーについて言及しました。ForkJoinTask を自分で実装している場合は、RecursiveTask または RecursiveAction を参照してください。compute() を実装すると、各タスクの結果を返す代わりに、compute() 関数の最後にあるコレクターに簡単に転送できます。
ただし、コレクターが同時に呼び出されることを考慮する必要があります。値の追加または完了カウントのカウントについては、java.util.concurrent.atomicを参照してください。同期ブロックの使用は避けてください。そうしないと、すべてのタスクがこの単一のボトルネックを待つ必要があり、1 つの CPU のみが動作し続けます。
結果の伝播には、結果を返すよりも多くの問題が伴うと思います (FJPool がこれを処理するため)。さらに、最終結果がどの時点で完了したかを決定する (および外部に伝達する) ことが難しくなります。