Ellen Spertus
Ellen Spertus

Reputation: 6815

Do different threads see the same version of a object referenced by a local variable?

Are multiple threads guaranteed to see the same version of a shared object to which they have a reference? Here is a code sample:

public static void main(String[] args) {
    final AtomicBoolean flag = new AtomicBoolean(false);

    new Thread(){
      public void run() { possibly read and mutate flag }
    }.start();

    new Thread(){
      public void run() { possibly read and mutate flag }
    }.start();

    while (!flag.get()) {
      Thread.yield();
    }
}

To be clear, I am wondering whether writes by the child threads to the shared object are seen by the parent and sibling threads.

Upvotes: 1

Views: 1846

Answers (4)

Stephen C
Stephen C

Reputation: 719281

Are multiple threads guaranteed to see the same version of a shared local variable in their scope.

In general, it depends on what you mean by "the same version". It also depends on the nature of the variable (e.g. how it is declared and initialized) ... and on how the threads use it.

(In general, Java doesn't do "versions" of variables. A thread accessing a shared variable or object will either see the latest state, or it won't. If it sees a state that isn't the latest state, then there are no guarantees as to what it will see. In particular, it may see something that doesn't directly correspond to any notional version of the object ... due to word-tearing and other cache-related memory artefacts.)


In your example you are using a final local variable within an inner class (in this case you have two anonymous inner classes). When you do that, the compiler creates a corresponding synthetic variable in the inner class that is initialized with the value of the variable in the method scope. The compiled inner class then refers to the value of the synthetic variable instead of the original variable.

In your example, it is guaranteed that the inner classes (e.g. your threads) will see the same (reference) value as in the original variable. Furthermore, it is guaranteed that they will (at least initially) see a consistent snapshot of whatever object it is that it references. (And since it is an AtomicXxxx class, it will always be consistent for all threads that can access it. Guaranteed.)


OK, so what about other cases:

  • If flag was a static or instance field that was also final, then we wouldn't have synthetic variables, and each nested class would be referencing the same shared variable. But it would all still work.

  • If flag was a static or instance field and it wasn't final, but nothing changed the field (after creating of the threads) then it would still be OK. (Though you could argue that this is fragile ... because something could change the field.)

  • If flag was a static or instance field and it wasn't final or volatile, then the threads would initially see the same state as the parent thread. But if either the original thread or any of the other threads changed the variable (etcetera), then the others are not guaranteed to see the new state ... unless they respective threads synchronize properly.


I would like to know if changes to flag made in one thread are seen immediately by the other two threads.

As I said above, it depends ...

  • In your example, the answer is "yes", because you use a final reference to AtomicBoolean.

  • If you had declared flag as a boolean and marked it as volatile, then the answer would be "yes".

  • If you had declared flag as a boolean and non-volatile, then the answer would be "no".

  • If flag was a final reference to an ordinary object with a mutable non-volatile boolean field, then the answer would also be "no". (The threads would all see the same object, but they wouldn't consistently see the latest state. The solution would be to use synchronized getters and setters, or equivalent.)

Upvotes: 2

user2864740
user2864740

Reputation: 61955

The local variable is not shared1, and being final means that there would be no worry of it changing even if it was the case. (A question about member variables would result in a different response although, excluding constructor leakage, a final member would provide the same guarantees.)

The same object is shared across threads; it will be the same object and will adhere to the defined AtomicBoolean contract.

A boolean value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables.

In short the package documentation specifies the following which in turn guarantees happens-before relationships.

  • get has the memory effects of reading a volatile variable.
  • set has the memory effects of writing (assigning) a volatile variable.

There are many questions relating to the thread-safey of volatile (and AtomicXYZ objects), eg. see Is a volatile int in Java thread-safe? and Is AtomicBoolean needed to create a cancellable thread?


1 Anonymous types, including Java 8 lambdas, do not create closures/lexical bindings to variables in scope and as such are not capable of sharing local variables; rather variables are synthesized with the value of the final (or effectively final) variable from the enclosing scope which is bound when the anonymous type is instantiated.

Upvotes: 0

markspace
markspace

Reputation: 11030

In this case, yes. The Java Language Specification says that calling Thread.start() synchronizes-with all previous actions on the calling thread:

An action that starts a thread synchronizes-with the first action in the thread it starts.

This creates a happens-before relationship between all writes on your main thread (including any writes the constructor of the AtomicBoolean made to initialize itself) are made visible to the thread your main thread started.

A call to start() on a thread happens-before any actions in the started thread.

So basically you are good to go. Your AtomicBoolean object is visible to both threads, and they both see the same object.

This pattern is called Safe Publication, btw. You use it to safely publish an object you create (like your AtomicBoolean) so that other threads can see it. (And yes, Thread.start() isn't on the list there of ways to safely publish an object because Thread.start() isn't general enough. But it's the same idea, and works the same way.)

Upvotes: 0

Nebula Era
Nebula Era

Reputation: 263

Yes, the two threads share the same final AtomicBoolean which is a class used to set the truth value. The variable flag itself can't be recreated because it is final. But you can perform actions on it to set value. Just like a final int[] can't be assigned to different size but you can change the value of what's inside.

final AtomicBoolean flag = new AtomicBoolean(false);
new Thread(){
     public void run(){
         try {
            Thread.sleep(100);
         } catch (InterruptedException e) {
        // TODO Auto-generated catch block
            e.printStackTrace();
         }
         flag.set(true);    
     }
 }.start();
 new Thread(){
     public void run(){
         flag.set(false);   
     }
 }.start();
 Thread.sleep(200);// comment this line, you see different results
 System.out.println(flag);

Upvotes: 1

Related Questions