David David - 1 year ago 65
Java Question

is java AtomicReference thread safe when used within parallelStream?

I used the parallelStream to get the longest string in an array, the code is following, each time it runs, I get different result.

supposes to be thread safe even when used within parallelStream? But why does this happen?

public static void main(String[] args) {
AtomicReference<String> longest = new AtomicReference<>();
LongAccumulator accumulator = new LongAccumulator(Math::max, 0);
List<String> words = Arrays.asList("him", "he", "thanks", "strings", "congratulations", "platform");
next -> longest.updateAndGet(
current -> {
String result = next.length() > accumulator.intValue() ? next : current;
return result;

for one time, I get “congratulations” printed, and some time I get “platform” printed.

Answer Source

You are invoking LongAccumulator.intValue() which is documented as:

Returns the current value as an int after a narrowing primitive conversion.

and following the linke to the get() method we will learn:

Returns the current value. The returned value is NOT an atomic snapshot; invocation in the absence of concurrent updates returns an accurate result, but concurrent updates that occur while the value is being calculated might not be incorporated.

So while the AtomicReference.updateAndGet operation is thread safe, your concurrent invocation of LongAccumulator.intValue() and LongAccumulator.accumulate is not. A LongAccumulator is intended for performing concurrent accumulate operations, followed by fetching the result after all accumulate operations have been finished. Note that even if get() was returning a correct snapshot, the fact that the invocation of intValue() and the subsequent accumulate() are two distinct, hence non-atomic, operations made the operation still prone to data races.

In most cases, if you find yourself trying to manipulate data structures in a forEach, you are using the wrong tool for the job, making the code unnecessarily complicated and error prone. As Clayn hinted in a comment, a words.parallelStream().max(Comparator.comparingInt(String::l‌​ength)) will do the job concisely and correct.