Search code examples
javamultithreadingvolatile

Is volatile not needed for objects' members but only on primitive members?


My Code is

package threadrelated;
import threadrelated.lockrelated.MyNonBlockingQueue;

public class VolatileTester extends Thread {

 MyNonBlockingQueue mbq ;

 public static void main(String[] args) throws InterruptedException {

    VolatileTester vt = new VolatileTester();
    vt.mbq = new MyNonBlockingQueue(10);
    System.out.println(Thread.currentThread().getName()+" "+vt.mbq);
    Thread t1 = new Thread(vt,"First");
    Thread t2 = new Thread(vt,"Secondz");
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(Thread.currentThread().getName()+" "+vt.mbq);

}
@Override
public void run() {
    System.out.println(Thread.currentThread().getName()+" before    "+mbq);
    mbq = new MyNonBlockingQueue(20);
    try {
        Thread.sleep(TimeUnit.SECONDS.toMillis(10));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+" after   "+mbq);
}

}

Output is

main threadrelated.lockrelated.MyNonBlockingQueue@72fcb1f4
Secondz before    threadrelated.lockrelated.MyNonBlockingQueue@72fcb1f4
First before    threadrelated.lockrelated.MyNonBlockingQueue@72fcb1f4
Secondz after   threadrelated.lockrelated.MyNonBlockingQueue@7100650c
First after   threadrelated.lockrelated.MyNonBlockingQueue@7100650c
main threadrelated.lockrelated.MyNonBlockingQueue@7100650c

It shows that when First thread assigns member variable to new object, same is visible to other thread. Even if "mbq" is not declared as volatile.

I used breakpoints to try different sequence of operations. But my observation is that one thread can immediately see impact of other thread.

Is volatile not needed for class members which are object ? Are they always synchronized to main memory ? Volatile needed only for primitive member variables (int, long, boolean etc. ? )


Solution

  • It's just as necessary for references as it is for primitives. The fact that your output doesn't show a visibility problem doesn't prove one doesn't exist. In general, it's very difficult to prove non-existence of a concurrency bug. But here's a simple counterproof showing the necessity of volatile:

    public class Test {
        static volatile Object ref;
    
        public static void main(String[] args) {
            // spin until ref is updated
            new Thread(() -> {
                while (ref == null);
                System.out.println("done");
            }).start();
    
            // wait a second, then update ref
            new Thread(() -> {
                try { Thread.sleep(1000); } catch (Exception e) {}
                ref = new Object();
            }).start();
        }
    }
    

    This program runs for a second, then prints "done". Remove volatile and it won't terminate because the first thread never sees the updated ref value. (Disclaimer: As with any concurrency test, results may vary.)