0
class C {
  Object o;
  public void set(Object o){
     if(this.o == null){ 
        this.o = o;
     }
  }
  public Object get(){
     return o;
  }
}

C c = new C();

C c = new C();  

Thread#1
Object o1 = c.get(); // 1
Object o2 = c.get(); // 2

Thread#2
c.set(new Object());

Is it possible that o2 == null && o1 != null? Why?


To make it clear what I mean I edited:

What if we have the following situation:

C c = new C(); // it is given at beginning 
1) Object o2 = c.o; // o2 is null. This operation was **reordered** before O `o1 = c.o. The JVM can do it because JMM allows do it. 
2) c.o = new Object()` //Thread #2 was executed 
3) O o1 = c.o // o1 is not null while o2 is.
2
  • I don't see how it's possible that get() ever returns anything except for null. o is never initialized and set() only modifies o if the argument is null Commented Jul 18, 2017 at 19:11
  • No, because c1 is retrieved before c2 and nothing can revert it to null in any other thread. However c2 could be non null while c1 is null Commented Jul 18, 2017 at 19:11

1 Answer 1

2

It is not possible, despite the fact that you have a data race.

The data race is because your gets and sets around o aren't synchronized, which means there's no happens-before order with them. You could solve that either by having both methods be synchronized, or by making o volatile, or in a few other ways.

Absent synchronization, the JVM is allowed to reorder events as seen by other threads. From Thread1's perspective, you have the following events (with methods inlined for simplicity):

c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.o = new Object(); // from Thread2

Luckily for you, there are two restrictions that make this work:

  1. The c.c = null happens-before all other actions (see JLS 17.4.5).
  2. From a given thread's perspective, actions that happen on that thread always happen in the same order they appear in the code (also JLS 17.4.5). So for Thread1, o1 = c.o happens-before o2 = c.o. (Thread2 would not have to see those reads in that order... but it never sees o1 or o2 at all, so there's no problem there.)

The first of those means that we can't take the c.o = null action and order it after c.c = new Object(). The second means that the reordering you mention at the bottom of your post is not allowed, from Thread1's perspective (and of course, Thread1 is the only thread that sees anything about o1 or o2).

Combining those two restrictions, we can see that if Thread1 ever sees c.o be non-null, then it will never see it revert back to null again. If o1 is non-null, then so must o2 be.

Note that this is pretty fickle. For instance, let's say that rather than only setting c.o once, Thread2 set it twice:

c.set("one");
c.set("two");

In that case, it would be possible to see o1 be "two" while o2 is "one". That's because the operations there are:

c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.c = "one"; // from Thread2
c.c = "two"; // from Thread2

The JVM can reorder the items from Thread2 however it sees fit, so long as they don't come before that c.c = null. In particular, this is valid:

c.o = null; // initial value
c.c = "two"; // from Thread2
o1 = c.o;
c.c = "one"; // from Thread2
o2 = c.o;

Removing the data race, by synchronizing the gets and sets to o, will fix that.

Sign up to request clarification or add additional context in comments.

5 Comments

@Gilgamesz Interesting, you edited it but still didn't address the fact that get() is typed to return Object, but when you invoke it, you try to assign it to a C. Did I misinterpret that it should be Object c1 = c.get()?
@Gilgamesz I edited my answer to explain why it's still safe.
"Actions that happen on a thread always formally happen-before in the same order they appear in the code (also JLS 17.4.5), so o1 = c.o happens-before o2 = c.o." You are not right. Please consider output for simple program: int x, y; Thread #1: x = 1; y = 1; Thread #2: print_without_sync(y); print_without_sync(x)
In your example, Thread 1 will see the write to x as happening before the write to y. The problem is that Thread 2 will not necessarily see that order. I've clarified the language in my answer ("From a given thread's perspective...").
@Gilgamesz But in doing so, you taught me about JCStress -- I didn't know about that tool. Very cool stuff, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.