Java Reference
In-Depth Information
required to transfer the data from one core to another. In general, there are many cases where it
isn't possible or convenient to use parallelization. But before you use a parallel Stream to make
your code faster, you have to be sure that you're using it correctly; it's not helpful to produce a
result in less time if the result will be wrong. Let's look at a common pitfall.
7.1.3. Using parallel streams correctly
The main cause of errors generated by misuse of parallel streams is the use of algorithms that
mutate some shared state. Here's another way to implement the sum of the first n natural
numbers but by mutating a shared accumulator:
public static long sideEffectSum(long n) {
Accumulator accumulator = new Accumulator();
LongStream.rangeClosed(1, n).forEach(accumulator::add);
return accumulator.total;
}
public class Accumulator {
public long total = 0;
public void add(long value) { total += value; }
}
It's quite common to write this sort of code, especially for developers who are familiar with
imperative programming paradigms. This code closely resembles what you're used to doing
when iterating imperatively a list of numbers: you initialize an accumulator and traverse the
elements in the list one by one, adding them on the accumulator.
What's wrong with this code? Unfortunately, it's irretrievably broken because it's fundamentally
sequential. You have a data race on every access of total. And if you try to fix that with
synchronization, you'll lose all your parallelism. To understand this, let's try to turn the Stream
into a parallel one:
public static long sideEffectParallelSum(long n) {
Accumulator accumulator = new Accumulator();
LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
return accumulator.total;
}
Try to run this last method with the harness of listing 7.1 , also printing the result of each
execution:
 
Search WWH ::




Custom Search