Search code examples
javamultithreadingsynchronizationvolatileatomic

Volatile keyword atomicity


I am trying to learn volatile field modifier in multi-threading. I came across this statement:

Volatile is preferred in cases when one thread reads and writes a shared variable and other threads just read the same. Whereas if there are more than 2 threads performing read and write both on the shared variable then only volatile is not enough, you need to have synchronization as well.

I am aware that volatile provides visibility and happens-before guarantee, but is it possible to give a simple small example of code to demonstrate the above statements wherein a synchronized block is needed?


Solution

  • Let's say you have int i and two threads, you expect every one read i and set i = i + 1.

    Like this:

    public class Main {
    
        private static volatile int i = 0;
    
        public static void main(String[] args) throws Exception{
    
            Runnable  first = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Thread_1 see i = " + i);
                    i++;
                    System.out.println("Thread_1 set i = " + i);
                }
            };
    
            Runnable second = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Thread_2 see i = " + i);
                    i++;
                    System.out.println("Thread_2 set i = " + i);
                }
            };
            new Thread(first).start();
            new Thread(second).start();
        }
    }
    

    The result is:

    Thread_1 see i = 0
    Thread_2 see i = 0
    Thread_1 set i = 1
    Thread_2 set i = 2
    

    As you see, Thread_2 get 0 and set 2(because Thread_1 has updated i to 1), which is not expected.


    After adding syncronization,

    public class Main {
    
        private static volatile int i = 0;
    
        public static void main(String[] args) throws Exception{
    
            Runnable  first = new Runnable() {
                @Override
                public void run() {
                    synchronized (Main.class) {
                        System.out.println("Thread_1 see i = " + i);
                        i++;
                        System.out.println("Thread_1 set i = " + i);
                    }
                }
            };
    
            Runnable second = new Runnable() {
                @Override
                public void run() {
                    synchronized (Main.class) {
                        System.out.println("Thread_2 see i = " + i);
                        i++;
                        System.out.println("Thread_2 set i = " + i);
                    }
                }
            };
            new Thread(first).start();
            new Thread(second).start();
        }
    }
    

    It works:

    Thread_2 see i = 0
    Thread_2 set i = 1
    Thread_1 see i = 1
    Thread_1 set i = 2