James Raitsev
James Raitsev

Reputation: 96391

Uses of volatile without synchronization

Knowing that

Reads and writes are atomic for all variables declared volatile

Question1: Can this be understood as if

private volatile int x = 0;

x++; operation is atomic?

And that

Marking variable volatile does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible.

Question2: I wonder under what circumstances (if any) it is possible to see a variable marked volatile and not see any methods of blocks marked synchronized (that attempt to access/ modify the variable)?

In other words, should all variables that need to be protected from concurrent modification be marked volatile?

Upvotes: 11

Views: 1273

Answers (3)

Andrea Alciato
Andrea Alciato

Reputation: 199

As a quickly testable example that may illustrate the previous answers, this yields always a final count of 8:

import java.util.concurrent.atomic.AtomicInteger;


public class ThreadTest_synchronize {

public static void main(String[] args) {

    ThreadTest_synchronize tt = new ThreadTest_synchronize ();
    try {
        tt.go();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

private void go() throws InterruptedException{

    MyRunnable t = new MyRunnable();
    Thread myThread_1 = new Thread( t, "t1");
    Thread myThread_2 = new Thread( t, "t2");
    myThread_1.start();
    myThread_2.start();
    myThread_1.join();
    myThread_2.join();
    System.out.println("Processing count="+t.getCount());       

}

private class MyRunnable implements Runnable{

    private AtomicInteger count=new AtomicInteger(0);

    @Override
    public  void run() {
        for(int i=1; i< 5; i++){
            doSomething(i);
            count.getAndAdd(1);
        }           
    }


    public AtomicInteger getCount() {
        return this.count;
    }


    private void doSomething(int i) {
        try {
            Thread.sleep(i*300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}       

}

while this generally does not:

public class ThreadTest_volatile {

public static void main(String[] args) {

    ThreadTest_volatile tt = new ThreadTest_volatile ();
    try {
        tt.go();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

private void go() throws InterruptedException{

    MyRunnable t = new MyRunnable();
    Thread myThread_1 = new Thread( t, "t1");
    Thread myThread_2 = new Thread( t, "t2");
    myThread_1.start();
    myThread_2.start();
    myThread_1.join();
    myThread_2.join();
    System.out.println("Processing count="+t.getCount());       

}

private class MyRunnable implements Runnable{

    private volatile int count = 0;


    @Override
    public  void run() {
        for(int i=1; i< 5; i++){
            doSomething(i);
            count++;
        }

    }

    private  int add(int count){
        return ++count;
    }


    public int getCount(){
        return count;
    }

    private void doSomething(int i) {

        try {
            Thread.sleep(i*300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


}

Upvotes: 0

Voo
Voo

Reputation: 30216

The volatile only gives you additional visibility guarantees, atomic writes/reads for longs/doubles (otherwise not guaranteed by the JLS, yes) and some memory order guarantees. No synchronization (it is possible though to build synchronization blocks starting with just volatile - Dekker's algorithm ) So no, it does not help you with x++ - that's still a read, inc and write and needs some form of synchronization.

One example of volatile is the famous double-checked locking, where we avoid synchronization most of the time because the ordering guarantees are all we need:

private volatile Helper helper = null;
public Helper getHelper() {
    if (helper == null) {
        synchronized(this) {
            if (helper == null) {
                helper = new Helper();
            }
        }
    }
    return helper;
}

An example where there's absolutely no synchronization involved, is a simple exit flag, here it's not about ordering guarantees but only about the guaranteed visibility

public volatile boolean exit = false;
public void run() {
   while (!exit) doStuff();
   // exit when exit set to true
}

If another thread sets exit = true the other thread doing the while loop is guaranteed to see the update - without volatile it may not.

Upvotes: 13

Louis Wasserman
Louis Wasserman

Reputation: 198033

x++; operation is atomic?

No. This reduces to x = x + 1. The read of x is atomic, and the write to x is atomic, but x = x + 1 as a whole is not atomic.

I wonder under what circumstances (if any) it is possible to see a variable marked volatile and not see any methods of blocks marked synchronized (that attempt to access/ modify the variable)?

Well, there are all kinds of approaches to concurrency that don't use synchronized. There's a wide variety of other locking utilities in Java, and lock-free algorithms that still require things like volatile: ConcurrentLinkedQueue is a specific example, though it makes extensive use of "magical" compareAndSet atomics.

Upvotes: 7

Related Questions