Skip to main content

Volatile Does Not Mean Atomic!

Here's a question I get a lot. Is the following code snippet "thread safe"?


volatile int v = 0;

Thread 1:
v++;

Thread 2:
v--;

The question asks what the possible results of this code are; the questioners usually want the answer "v can only be 0 after this code is run".

This isn't the way it works! If you do an increment of a volatile integer, you are actually performing three separate operations:

  1. Read the integer to a local.

  2. Increment the local.

  3. Write the integer back out to the volatile field.


So what you really have is this:

volatile int v = 0;

Thread 1:
r1 = v;
r2 = r1 + 1;
v = r2;

Thread 2:
r3 = v;
r4 = r3 - 1;
v = r4;

So, if Threads 1 and 2 both read v and see the value 0, then Thread 1 will write 1 to it and Thread 2 will write -1 to it. You are not guaranteed to see the value 0!

If you want an atomic increment (or decrement), you have to use the java.util.concurrent.atomic classes, which allow you to create object that represent numbers that can be incremented or decremented atomically. The VM is smart enough to replace the objects with plain ol' numbers (a process that I claim is called intrinsification), which it then uses atomic machine instructions to manipulate.

So beware!

ETA: There was a bit of confusion about atomicity after I posted this. For more on atomicity, visibility and ordering, check out this post.

Comments

Jonathan said…
Hi Jeremy. The Java spec says this:

Another approach would be to declare i and j to be volatile:

class Test {
  static volatile int i = 0, j = 0;
  static void one() { i++; j++; }
  static void two() {
   System.out.println("i=" + i + " j=" + j);
  }
}

This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i and j occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j is never greater than that for i, because each update to i must be reflected in the shared value for i before the update to j occurs.

This seems to conflict your statement.
Jeremy Manson said…
Hi, Jonathan.

You are misreading the spec. This is referring to an ordering constraint, not an atomicity constraint. I should do a full posting about what the difference is, but here's the Reader's Digest version:

The example you gave is about what happens when one thread writes to two volatile variables, and another thread reads from those variables. In that case, the updates to the variables will appear to occur in the same order to both threads (so you can't see the value 0 for i and the value 1 for j, because that would mean that other thread saw the update to i without seeing the update to j).

My posting was about what happens when two threads write to one volatile variable simultaneously. Because two threads are doing it, there's no guaranteed ordering between the respective increment operations, so you end up with a result that depends on the interleaving of the two threads -- you can get a different result depending on what the interleavings are.
Anonymous said…
This comment has been removed by a blog administrator.
Jaime Hablutzel said…
If volatile doesn't guarantee the atomicity... so what it does?? I'm a little confused
Jeremy Manson said…
Volatile does this:

http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html
yura said…
Hi Jeremy, thank you for the information in this post. I found it useful for myself, can you help me with following questions?
Question(1)
Can you help me understand a problem in the following snipped. I think it is ordering problem, because I see delta in results only equals to "1". I'm not saying that the following code is protected from the visibility problem, but what I'm seeing in the results is related to ordering, correct?

When I use the local synchronization on "lock" object (commented out code) or volatile definition, I don't see the problem.

public class ShowVolatile {

private /*volatile*/ long x = 0;
private /*volatile*/ long y = 0;
private /*volatile*/ static boolean b = false;

//private static Object lock = new Object();
private static ShowVolatile sv = new ShowVolatile();

public static void main(String... args) {

System.out.println("start TH1");
new Thread(new Runnable() {
public void run() {
System.out.println("started TH1");
while (true) {
//synchronized (lock) {
sv.x += 1;
sv.y += 1;
b = true;
//}
while (b) {
Thread.yield();
}
}
}
}).start();

while (!b) {
Thread.yield();
}

System.out.println("start TH2");
new Thread(new Runnable() {
public void run() {
System.out.println("started TH2");
while (true) {

while (!b) {
Thread.yield();
}

//synchronized (lock) {
long x = sv.x;
long y = sv.y;
if ((x != y)) {
System.out.println("got it, x: " + x + " y: " + y);
System.out.println(" sv.x: " + sv.x + " sv.y: " + sv.y);
System.out.println("-------------------------------------------");
// System.exit(-1);
}

b = false;
//}
}
}
}).start();
}
}

results on linux x64 (kernel 3.0), i7, oracle java 1.7:
got it, x: 3193371056 y: 3193371055
got it, sv.x: 3193371056 sv.y: 3193371056
-------------------------------------------
got it, x: 3548566839 y: 3548566838
got it, sv.x: 3548566839 sv.y: 3548566839
-------------------------------------------
got it, x: 3556046058 y: 3556046057
got it, sv.x: 3556046058 sv.y: 3556046058
-------------------------------------------
got it, x: 3566294270 y: 3566294269
got it, sv.x: 3566294270 sv.y: 3566294270
-------------------------------------------

Question (2)
In case I un-comment the "lock" object & synchronized blocks, I don't see the problem. I think it is because it fixes the ordering problem. Does this means that this change fixes the visibility problem as well? or I should use the volatile keyword for x, y and b (or at least for one of them) to fix the visibility problem?
Jeremy Manson said…
@yura - this isn't really an ordering issue. This is a plain old lost update. TH2 is reading x and y between the increment to x and the increment to y, so they are different. Locking fixes this by ensuring that they can only be read after they are both incremented.
~SS~ said…
Could you please also explain when an atomic variables need to be Volatile or Vice versa?
Jeremy Manson said…
@~SS~: If you are still paying attention, I'm not sure I know what you mean by an "atomic variable". If you mean an object with a type of java.util.concurrent.atomic.AtomicSomething, then it is probably the reference to that object that will be volatile, not the object itself. The reference to that variable needs to be protected by synchronization just like any other variable does.
Anonymous said…
Hi Jeremy,

I found your blog when trying to find some answers to my questions on java concurrency & found your blog very useful.
Can you please help me understand this statement :

"It is safe to perform read-modify-write operations on shared volatile variables as long as you can ensure that the volatile variable is only written from a SINGLE thread. In this case you are confining the modifications to a single thread to prevent race conditions, & the visibility guarantees for volatile variables ensures that other threads see the most up-to-date value."

Now as i understand the "read-modify-write operation" they are referring to is x++ or the x-- operation which as you explained above is actually a 3 step operation -
r1 = v; //step1
r2 = r1 + 1; //step2
v = r2; //step3

So if only 1 thread (say thread1) is doing the x++ operation & while thread1 is in the middle of the operation say at step2 above, at that point in time another thread say thread2 reads x, then thread2 will not see the up-to-date value, even if only 1 thread is doing the writes/modifications.
The above statement says "visibility guarantees for volatile variables" ...
which i understand that when a variable is declared as volatile, then the runtime & CPU are told not to cache that variable but instead store it in main memory (and no re-ordering of statements related to volatile).
So even if a single thread is writing a volatile variable to the main memory, but if another thread reads x while thread1 was at step2, it would still see corrupt data.

So i am unclear how a single thread doing writes to a volatile variable does not have race conditions.

Thanks in advance.
Ila
Jeremy Manson said…
@ila - it wouldn't see a corrupt value. The value would be stale, but not corrupt.

This is a race condition, but it may be one that is acceptable in your program.

Note that it is *not* a data race, which has a more restrictive definition.

Popular posts from this blog

Double Checked Locking

I still get a lot of questions about whether double-checked locking works in Java, and I should probably post something to clear it up. And I'll plug Josh Bloch's new book, too. Double Checked Locking is this idiom: // Broken -- Do Not Use! class Foo {   private Helper helper = null;   public Helper getHelper() {     if (helper == null) {       synchronized(this) {         if (helper == null) {           helper = new Helper();         }       }     }   return helper; } The point of this code is to avoid synchronization when the object has already been constructed. This code doesn't work in Java. The basic principle is that compiler transformations (this includes the JIT, which is the optimizer that the JVM uses...

What Volatile Means in Java

Today, I'm going to talk about what volatile means in Java. I've sort-of covered this in other posts, such as my posting on the ++ operator , my post on double-checked locking and the like, but I've never really addressed it directly. First, you have to understand a little something about the Java memory model. I've struggled a bit over the years to explain it briefly and well. As of today, the best way I can think of to describe it is if you imagine it this way: Each thread in Java takes place in a separate memory space (this is clearly untrue, so bear with me on this one). You need to use special mechanisms to guarantee that communication happens between these threads, as you would on a message passing system. Memory writes that happen in one thread can "leak through" and be seen by another thread, but this is by no means guaranteed. Without explicit communication, you can't guarantee which writes get seen by other threads, or even the order in whic...

Atomicity, Visibility and Ordering

(Note: I've cribbed this from my doctoral dissertation. I tried to edit it heavily to ease up on the mangled academic syntax required by thesis committees, but I may have missed some / badly edited in places. Let me know if there is something confusingly written or just plain confusing, and I'll try to untangle it.) There are these three concepts, you see. And they are fundamental to correct concurrent programming. When a concurrent program is not correctly written, the errors tend to fall into one of the three categories: atomicity , visibility , or ordering . Atomicity deals with which actions and sets of actions have indivisible effects. This is the aspect of concurrency most familiar to programmers: it is usually thought of in terms of mutual exclusion. Visibility determines when the effects of one thread can be seen by another. Ordering determines when actions in one thread can be seen to occur out of order with respect to another. Let's talk about t...