Do I have to synchronize on a List that is read by a stream?
Yes, you have to synchronize access to the list when using streams in the same way you would do without streams. Synchronization should be taken care of by a user.
A stream by itself doesn't guarantee to create any copy of the original sequence. It can make a copy during some intermediate computations (e.g. sort
), but you should not rely on that. And doing this for each usage of a stream would be a waste of resources since streams are not reusable.
If a user wants a stream to operate over a copy, they have to manually create a copy or use CopyOnWriteArrayList
instead of ArrayList
, for example.
Moreover, keep in mind that streams are lazy. The underlying sequence is not accessed until a terminal operation (e.g. collect
, forEach
) is executed.
Stream operations use spliterator()
method internally.
Here is the spliterator()
method from ArrayList
:
public Spliterator<E> spliterator() {
checkForComodification();
return new ArrayListSpliterator<E>(ArrayList.this, offset,
offset + this.size, this.modCount);
}
It checks for comodification, so it looks like stream()
operations have to be inside synchronized blocks in your case.
Also, spliterator()
of SynchronizedCollection
(in Collections
) has comment
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
which is analogous to the comment in iterator()
:
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}
which shows the same: synchronization is needed around stream()
operations (at least, if iterator()
requires such a synchronization).
And the most convincing: stream()
method from SynchronizedCollection
:
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!
}