-1

Consider the following code:

public class StreamDemo {
    public static void main(String[] args) {
        StreamObject obj = new StreamObject();
        obj.setName("mystream");

        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

        list.parallelStream().forEach(l -> {
            obj.setId(l);
            System.out.println(obj + Thread.currentThread().getName());
        });
    }

    static public class StreamObject {
        private String name;
        private Integer id;

        // getters, setters, toString()
    }
}

When it is compiled and run with java 11, it returns the following:

StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-23
StreamObject{name='mystream', id=4}main
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-5
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-19

But with java 1.8, it returns different result:

StreamObject{name='mystream', id=3}main
StreamObject{name='mystream', id=5}ForkJoinPool.commonPool-worker-2
StreamObject{name='mystream', id=2}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=1}ForkJoinPool.commonPool-worker-11
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-4

Why results are different?

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Rustam
  • 150
  • 2
  • 10
  • Try taking a look at It is 1.8 vs 9 but should give you more idea about how streams work in 1.8 search for example of inconsistent behavior java-8 stream? – QuickSilver Apr 26 '20 at 11:08

4 Answers4

7

Both results are consistent with the Java Memory Model.

One possible ordering in which execution occurs is:

T1 calls setId
T1 prints
T2 calls setId
T2 prints
...
T5 calls setId
T5 prints

but, because you don't do anything to ensure that the set and print occur atomically, the following is also allowed (as are many other orderings):

T3 calls setId
T1 calls setId
T2 calls setId
T5 calls setId
T4 calls setId

T1 prints
T1 prints
...
T5 prints

So, the reason they're different is because the specification doesn't require them to be the same; and some subtle (or perhaps not-so-subtle) implementation (or environmental) difference means they execute differently.

But, you say, what is the implementation difference? That's not something you should need to care about (which sounds like bluster to cover for not knowing: I really don't). You should care about the Java Memory Model, because that gives the guaranteed properties.

For example, if you want the "Java 8" behaviour, you can synchronize the threads on a common monitor, for example obj:

list.parallelStream().forEach(l -> {
    synchronized (obj) {
        obj.setId(l);
        System.out.println(obj + Thread.currentThread().getName());
    }
});

Of course, the threads still will execute in an arbitrary order; but each thread will print the value that it set.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
1

There is no difference in Java 8 and Java 11, for each run we are getting different results. If we want to print properly we can use synchronize block, but we will lose the benefit of parallelStream in this case.

JAVA 8

enter image description here

enter image description here

JAVA 11

enter image description here

enter image description here

SANN3
  • 9,459
  • 6
  • 61
  • 97
1

Note that this behaviour is explicitly non deterministic as per the javadoc, so both outputs are valid execution orders.

The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization.

Dorian Gray
  • 2,913
  • 1
  • 9
  • 25
0

I suppose that if you didn't use the forEachOrdered method and instead you're using forEach on the stream it means that each time you should receive different values no matter which JDK you will use.

Marcin Erbel
  • 1,597
  • 6
  • 32
  • 51
  • 1
    I think my question wasn't clear enough. The question is: why the result for java 11 is different from the result for 1.8. If you look closely, id field is the same in java 11, but different in 1.8. – Rustam Apr 26 '20 at 08:57