Bass
Bass

Reputation: 5338

Safe publication of a not-threadsafe standard collection

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:

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

Answers (2)

Vladimir Dolzhenko
Vladimir Dolzhenko

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

Peter Lawrey
Peter Lawrey

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

Related Questions