1

I have a code like the one below where an object is shared among two threads (the main thread and the Monitor thread). Do I have to declare MyObject globally and make it volatile to ensure it will be pushed to memory? Otherwise the if statement can print "Not null" if MyObject is only locally accessed by the thread and is not declared volatile, right?

public static void main(String[] args) {
    MyObject obj = MyObjectFactory.createObject();
    new Monitor(obj).start();
    Thread.sleep(500); 
    if(obj == null) 
        System.out.println("Null");
    else
        System.out.println("Not null");
}

public void doSomethingWithObject(MyObject obj) {
    obj = null;
}

private class Monitor extends Thread {
   public Monitor(MyObject obj) {
       this.obj=obj;
   } 
   public void run() {
       doSomethingWithObject(obj);
   }
}

Note: The code example may not compile since I wrote it myself here on Stackoverflow. Consider it as a mix of pseudo code and real code.

Rox
  • 2,647
  • 15
  • 50
  • 85
  • It would be much easier to tell what you are attempting to demonstrate with a code example that compiles :) – Affe Sep 13 '12 at 07:08
  • @Affe: sorry, I made a note in my post where I explain why it not may compile. :-) – Rox Sep 13 '12 at 07:13

4 Answers4

2

The instance is shared but the references to it are not. Example:

 String a = "hello";
 String b = a;

 b = null; // doesn't affect a

a and b are references to the same instance; changing one reference has no effect on the instance or any other references to the same instance.

So if you want to share state between threads, you will have to create a field inside MyObject which has to be volatile:

class MyObject { public volatile int shared; }


public void doSomethingWithObject(MyObject obj) {
    obj.shared = 1; // main() can see this
}

Note that volatile just works for some types (references and all primitives except long). Since this is easy to get wrong, you should have a look at types in java.util.concurrent.atomic.

[EDIT] What I said above isn't correct. Instead, using volatile with long works as expected for Java 5 and better. This is the only way to ensure atomic read/writes for this type. See this question for references: Is there any point in using a volatile long?

Kudos go to Affe for pointing that out. Thanks.

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • @Rox only on primitive variables regardless their byte-size – Nir Alfasi Sep 13 '12 at 07:18
  • `volatile` as such works but reads and writes to it aren't atomic so you can get unexpected results: http://stackoverflow.com/questions/3038203/is-there-any-point-in-using-a-volatile-long – Aaron Digulla Sep 13 '12 at 07:18
  • 1
    The linked question says the opposite of your assertion. There definitely was a time in the past when it did not work as expected for 64 bit data, but was many years ago – Affe Sep 13 '12 at 07:20
  • @Affe: You're 100% correct; I fixed my answer. Sorry for the confusion. – Aaron Digulla Sep 13 '12 at 07:26
  • 1
    From the JLS (17.7) (from the referenced link): "Writes and reads of volatile long and double values are always atomic. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values." – nairbv Sep 13 '12 at 07:29
  • That at least demonstrates why everyone really should use the types in `java.util.concurrent.atomic` ;-) – Aaron Digulla Sep 13 '12 at 07:35
  • Your last comment (EDIT) is correct since Java 5, before that you could't count on volatile for read-update-write as an atomic operation since no lock was help. From Java 5 the implementation is equivalent to `synchronize`ing all read/write operations. – Nir Alfasi Sep 13 '12 at 17:27
1

If you want your object to go through a read-update-write scheme atomically, volatile won't cut it. You have to use synchronisation.

Volatility will ensure that the variable will not be cached in the current thread but it will not protect the variable from simultaneous updates, with the potential for the variable becoming something unexpected.

IBM's developerWorks has a useful article on the subject.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

Your example consists only one thread, Monitor, which is created and run in main().

"make it volatile to ensure it will be pushed to memory?" - on the contrary, when you declare a variable as volatile - it ensures that it's NOT being "pushed" (cached) to the thread-local memory, cause there might be other threads that will change the value of the variable.

In order to make sure you print the correct value of a variable you should synchronize the method doSomethingWithObject (change the signature of the method to):

public synchronized void doSomethingWithObject(MyObject obj)

or create synchronized blocks around:

obj = null;

and

this.obj=obj;
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
1

You would rather have to synchronize on the object to ensure it will be set to null before the if check. Setting it to volatile only means changes will be "seen" immediately by other threads, but it is very likely that the if check will be executed before the doSomethingWithObject call.

kpentchev
  • 3,040
  • 2
  • 24
  • 41