How can an immutable object with incomplete fields be unsafe?

let's say we have it

// 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 this question.

+7
source share
4 answers

Foo is a safe stream after it's published securely. For example, this program may print β€œunsafe” (it probably won’t use the hotspot / x86 combination) - if you do bar final, this will not 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(); } } 
+10
source

Due to JVM optimization, you can never assume that operations are performed in the order in which they are written, if it is not important for a single thread. Therefore, when you call the constructor and then pass the reference to the resulting object to another thread, the JVM may not actually write the value of foo.bar before it is needed inside the same thread .

This means that in a multi-threaded environment, the getBar method can be called before a value is written to it in the constructor.

+7
source

Most likely, you have your own answer, but I just want to add my explanation.

For an object (for your case) to be thread safe, it must:

  • Stay unchanged
  • Be well posted

Immutable - you did it like that. Cannot change the panel after installing it. This is pretty obvious.

Safe publishing . According to an example, the code is not published securely. Since the bar is not final, the compiler can freely reorder it at its discretion. The compiler can publish (write to main memory) a link to the Foo instance before writing to bar. This would mean that bar is null. So, first the link to Foo is written to the main memory, then there is a record in the bar. Between these two events, another thread can see that the stale bar is null.

If you add the final version, JMM will ensure that:

the values ​​of the final fields are guaranteed to be visible to other threads accessing the constructed object.

Or the final field prevents reordering. Thus, creating this final variable will ensure thread safety.

+4
source

From the link posted in the comments:

 class FinalFieldExample { final int x; int y; static FinalFieldExample f; public FinalFieldExample() { x = 3; y = 4; } static void writer() { f = new FinalFieldExample(); } static void reader() { if (f != null) { int i = fx; // guaranteed to see 3 int j = fy; // could see 0 } } } 

One thread can call writer() , and another thread can call reader() . The if condition in read () could be evaluated as true, but since y is not final, the initalizion object may not be completely completed (therefore, the object has not yet been published securely), and thus int j = 0 can happen, since it has not been initialized.

+1
source

All Articles