Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can an immutable object, with non-final fields, be thread unsafe?

say we have this

// This is trivially immutable.
public class Foo {
    private String bar;
    public Foo(String bar) {
        this.bar = bar;
    }
    public String getBar() {
        return bar;
    }
}

What makes this thread unsafe ? Following on from this question.

like image 775
NimChimpsky Avatar asked Apr 17 '13 14:04

NimChimpsky


People also ask

How are immutable objects thread-safe?

To put it simply, a class instance is immutable when its internal state can't be modified after it has been constructed. A MessageService object is effectively immutable since its state can't change after its construction. So, it's thread-safe.

Does all property of immutable object needs to be final?

No, it is not mandatory to have all properties final to create an immutable object. In immutable objects you should not allow users to modify the variables of the class. You can do this just by making variables private and not providing setter methods to modify them.

Can we use immutable objects in multithreading?

Immutable objects are useful in multithreaded applications because they can be safely accessed by several threads concurrently, without the need for locking or other synchronization.

Are final fields thread-safe?

Final variables are immutable references, so a variable declared final is safe to access from multiple threads. You can only read the variable, not write it.


2 Answers

Foo is thread safe once it has been safely published. For example, this program could print "unsafe" (it probably won't using a combination of hotspot/x86) - if you make bar final it can't happen:

public class UnsafePublication {

    static Foo foo;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (foo == null) {}
                if (!"abc".equals(foo.getBar())) System.out.println("unsafe");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                foo = new Foo("abc");
            }
        }).start();
    }
}
like image 156
assylias Avatar answered Sep 17 '22 13:09

assylias


Due to JVM optimization, you can never assume that operations are executed in the order they are written, unless it matters for the same thread. So when you call the constructor and then pass a reference to the resulting object to another thread, the JVM might not actually write the value of foo.bar before it is needed within the same thread.

That means that in a multithreaded environment, the getBar method could be called before the value in the constructor was written to it.

like image 40
Philipp Avatar answered Sep 21 '22 13:09

Philipp