3
class MyItem {
    private param1, param2, param3;
}

MyItem item = new MyItem();

computeParam1(item);
computeParam2(item);
computeParam3(item);
waitForAllParamsToBeSet();

Each of the steps is independent from each other, and each step write the paramter into the object as final result. The methods are completely different from their logic, no recursion.

How could I parallelize those steps, if possible at all?

membersound
  • 81,582
  • 193
  • 585
  • 1,120

3 Answers3

3

Start Futures and then wait for results before assigning.

Future<Type1> item1 = ComputeParam1();
Future<Type2> item2 = ComputeParam2();
Future<Type3> item2 = ComputeParam3();

MyItem item = new MyItem();

assignParam1(item1.get());
assignParam2(item2.get());
assignParam3(item3.get());
weston
  • 54,145
  • 21
  • 145
  • 203
2

As all computeParamX() accept one MyItem argument and have void return, they have a signature of Consumer<MyItem>. So you can parallelize their execution calling them in .forEach() of parallel stream, as follows:

final MyItem item = new MyItem();
Stream.<Consumer<MyItem>>of(this::computeParam1, this::computeParam2, this::computeParam3)
          .parallel()
          .forEach(c -> c.accept(item));

As .forEach() is terminal operation, it will block until all operations complete, so you can safely use item object after it returns.

Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • So now you are writing to `item` from several threads. We do not know enough about `MyItem` class to know this is safe. – weston Apr 06 '16 at 12:05
  • 1
    @weston yes, you're right. Before using this, one must ensure that `computeParamX` methods don't interfere. OP says they are independent, and I don't see the reason not to believe. – Alex Salauyou Apr 06 '16 at 12:10
  • The top reason to be suspicious is that if the calculations are independent then why is an instance passed in to them, rather than just assigned after calculation? i.e. in the question, why is it not: `item.setParam1(computeParam1());` etc? – weston Apr 06 '16 at 12:43
  • 1
    @weston yes, yes and yes, you have more correct design. I also like it. I won't be offended if OP accepts your answer rather than mine. My intention was to show how arbitrary methods may be represented as lambdas so they can be processed using Stream API. – Alex Salauyou Apr 06 '16 at 13:08
2

In Java 8 you could simply create your collection of tasks as next:

Collection<Runnable> tasks = Arrays.asList(
    () -> System.out.println("Compute param1"),
    () -> System.out.println("Compute param2"),
    () -> System.out.println("Compute param3")
);

Then launch the tasks in parallel

tasks.parallelStream().forEach(Runnable::run);
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • Note that with this approach it is not (only with work-arounds) possible to specify the thread pool: http://stackoverflow.com/questions/21163108/custom-thread-pool-in-java-8-parallel-stream – user140547 Apr 06 '16 at 12:04
  • 1
    This approach relies on ForkJoinPool that launchs as many threads as you have cores on the target machine corresponding to Runtime.getRuntime().availableProcessors() but you can still modify the default value thanks to System properties such as _java.util.concurrent.ForkJoinPool.common.parallelism_ more details here https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html – Nicolas Filotto Apr 06 '16 at 12:12
  • yes, but that thread is about the fact that every task using parallel stream executing in the default ForkJoinPool, which may be a concern if others tasks are slowing down or even deadlocking that pool. – user140547 Apr 06 '16 at 12:24
  • That's right: _With Great Power Comes Great Responsibility_ :-) – Nicolas Filotto Apr 06 '16 at 12:29