6
public class MyThread
{
    volatile static int i;

    public static class myT extends Thread
    {
        public void run ()
        {
            int j = 0;
            while(j<1000000){
                i++;
                j++;
            }
        }
    }

    public static void main (String[] argv)
    throws InterruptedException{
            i = 0;

            Thread my1 = new myT();
            Thread my2 = new myT();
            my1.start();
            my2.start();

            my1.join();
            my2.join();

            System.out.println("i = "+i);
    }
}

Since volatile builds happens-before relationship, the final value of i should be strictly 2000000. However, the actual result is nothing different from being without volatile for variable i. Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.

0

1 Answer 1

10

Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.

It is protected but unfortunately i++ is not an atomic operation. It is actually read/increment/store. So volatile is not going to save you from the race conditions between threads. You might get the following order of operations from your program:

  1. thread #1 reads i, gets 10
  2. right afterwards, thread #2 reads i, gets 10
  3. thread #1 increments i to 11
  4. thread #2 increments i to 11
  5. thread #1 stores 11 to i
  6. thread #2 stores 11 to i

As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?

What you should be using are AtomicInteger which allows you to safely increment from multiple threads.

static final AtomicInteger i = new AtomicInteger(0);
...
        for (int j = 0; j<1000000; j++) {
            i.incrementAndGet();
        }
Sign up to request clarification or add additional context in comments.

13 Comments

Only i needs to be an AtomicInteger; j is purely local to the thread.
I'm not sure how to answer @OneZero. Declaring it as volatile won't work because ++ is not atomic. You could synchronize on it every time you update it or use AtomicInteger. Making it volatile isn't enough.
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about what volitile is for. Not this.
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than a word on the underlying memory. double is the most common offender. Generally speaking, most concurrency issues are too much for volatile so look at synchronize and wait instead.
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have a double data and a volitile boolean ready and code that sets data first and ready = true second, it is certain that after you see ready turn to true, data will also have been set, even in a different thread. This is not guaranteed without the volatile keyword, where you can see ready true and then the value of data may not have propagated from the write thread.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.