5

There are many questions on Stackoverflow dating back to 2008 asking if unmodifiable collections are thread-safe, but JDK 10 (released in 2018) introduced a different beast: unmodifiable copies of existing collections.

List.copyOf() states:

If the given Collection is subsequently modified, the returned List will not reflect such modifications.

Does that mean that the return value of List.copyOf(), Set.copyOf(), Map.copyOf() are all thread-safe?

(I realize that the elements contained in the collection themselves are not guaranteed to be thread-safe.)

Gili
  • 86,244
  • 97
  • 390
  • 689
  • Since the JavaDoc of `copyOf` links to the same paragraph on "Unmodifiable Lists" than the one linked to in `of`, I see no reason to believe this behaves any different. – Joachim Sauer Sep 07 '22 at 16:40
  • @JoachimSauer Sorry, I don't understand what you mean. The Javadoc of the `of()` method does not explicitly state whether its return value is thread-safe either. – Gili Sep 07 '22 at 16:52
  • I don't know the answer, but how hard would it be for you to ensure that there is at least one "happens before" edge between one thread creating the immutable List, and some other thread reading it? If you can do that, then the problem is solved regardless of whether it actually _was_ a problem or not. – Solomon Slow Sep 07 '22 at 18:25
  • 1
    They are *immutable*. Immutable objects are intrinsically thread safe, as thread safety problems are caused by concurrent modifications. As you already said yourself, this does not necessarily apply to the contained elements. – Holger Sep 07 '22 at 19:49
  • @Holger, Even an immutable object must be "safely published." The object returned by `List.copyOf(...)` resides in read-write memory, and its fields were written-to immediately before the reference was returned. It's only "immutable" in the sense that there are no public methods available that will subsequently modify it. – Solomon Slow Sep 07 '22 at 20:47
  • @Holger I think Solomon is right. See https://stackoverflow.com/a/55870087/14731. So to summarize my understanding: so long as we "safely publish" immutable objects, they are guaranteed to be thread-safe. And `List.copyOf()`, `Set.copyOf()`, and `Map.copyOf()` return immutable objects. Is that correct? – Gili Sep 08 '22 at 01:53
  • 3
    @SolomonSlow but the question wasn’t whether these objects are immune to unsafe publishing but whether they are thread safe. They are and in fact, they are even immune to unsafe publishing due to the use of `final` fields, though this has not been explicitly specified. For comparison, a `ConcurrentHashMap` is not immune to unsafe publication, but nobody would say that `ConcurrentHashMap` was not thread safe. – Holger Sep 08 '22 at 07:07
  • [this](https://shipilev.net/blog/2014/safe-public-construction/) should help also – Eugene Sep 08 '22 at 10:30
  • @Holger, IMO, "thread safe" is easily and often misunderstood by newbies. Often, when they ask whether some Foobar class is thread safe, they are asking the wrong question. The objects returned by `List.copyOf(...)` and `List.of(...)` are as "safe" as anything can be _if_ they are properly handed off from the thread that created them to other threads, but that "if" is important. – Solomon Slow Sep 08 '22 at 12:39

1 Answers1

6

Since the collections created via, e.g. List.of(…) or List.copyOf(…) do not reflect subsequent changes to the specified array or collection and are immutable in general, it’s not possible to perform modifications after construction and in absence of concurrent modifications, there can’t be any thread safety issues.

The remaining question is whether the construction of these collections itself is immune to unsafe publication. Generally, you should use the proper constructs for publishing objects to other threads and not rely on unsafe publication.

But to recall, Unmodifiable Lists say

  • They are unmodifiable. Elements cannot be added, removed, or replaced. Calling any mutator method on the List will always cause UnsupportedOperationException to be thrown. However, if the contained elements are themselves mutable, this may cause the List's contents to appear to change.

and following the value-based link, we find:

Value-based Classes

Some classes, such as java.lang.Integer and java.time.LocalDate, are value-based. A value-based class has the following properties:

  • the class declares only final instance fields (though these may contain references to mutable objects);

Final fields are important for immutable objects intended to be immune to unsafe publication. As JLS§17.5. final Field Semantics specifies:

final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code.

But we still have to assume the intent “to implement thread-safe immutable objects” on the JDK developer’s side rather than, e.g. the intent to surprise developers by explicitly using final fields and then deliberately spoiling the safety guarantees.

If in doubt, just use safe publication. It won’t hurt.

And even if you already said this, it’s important to emphasize that this does not apply to the contained elements if they are mutable.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    Good answer. As an aside, anyone who wants to understand safe publication is encouraged to read https://shipilev.net/blog/2014/safe-public-construction/. I personally find it much easier to understand than reading the JLS directly. – Gili Sep 08 '22 at 14:29