Reputation: 5338
Is my understanding correct that the following code fragment is safe in terms of publication of a standard collection which is not inherently thread-safe (HashSet
in this example), because none of the threads modifies the collection after a memory barrier:
class C {
private final Set<String> strings;
C(final Set<String> strings) {
this.strings = new HashSet<>(strings);
}
void doSmthAsync() {
new Thread() {
@Override
public void run() {
for (final String s : strings) {
System.out.println(s);
}
}
}.start(); // Disregard the 2nd memory barrier caused by Thread.start()
}
}
while this one below is not?
class C {
private final Set<String> strings = new HashSet<>();
C(final Set<String> strings) {
this.strings.addAll(strings);
}
void doSmthAsync() {
new Thread() {
@Override
public void run() {
for (final String s : strings) {
System.out.println(s);
}
}
}.start(); // Disregard the 2nd memory barrier caused by Thread.start()
}
}
Update: the examples above are not completely correct because there's actually two memory barriers, not one:
Thread.start()
If, instead, in the doSmthAsync()
method we feed the collection to an already started thread (e.g. one from a background thread pool), it seems the consumer thread may see the final field in the state it had during initialization (i.e. an empty set in the 2nd case).
Upvotes: 2
Views: 94
Reputation: 36
JSR-133 Cookbook
http://gee.cs.oswego.edu/dl/jmm/cookbook.html
A store of a final field (inside a constructor) and, if the field is a reference, any store that this final can reference, cannot be reordered with a subsequent store (outside that constructor) of the reference to the object holding that field into a variable accessible to other threads. For example, you cannot reorder x.finalField = v; ... ; sharedRef = x; This comes into play for example when inlining constructors, where "..." spans the logical end of the constructor. You cannot move stores of finals within constructors down below a store outside of the constructor that might make the object visible to other threads.
That looks like two examples are equivalent in terms of safe publishing.
Upvotes: 1
Reputation: 533750
Based on your updated question, there is a possible race condition in the addAll
as this occurs after the final
field has been set. This doesn't mean you will see this race condition as it's behaviour is undefined and the JVM is free to add a memory barrier.
Anything which is read only before you start a Thread is thread safe.
BTW your examples are two ways of writing the same thing, so they are as thread safe as each other.
Upvotes: 2