3

I'm trying to illustrate the use and importance of volatile with an example that would really not give a good result if volatile was omitted.

But I'm not really used to using volatile. The idea of the following code is to cause an infinite loop if volatile is omitted, and be perfectly thread-safe if volatile is present. Is the following code thread-safe? Do you have any other realistic and short example of code that uses volatile and would give an obviously incorrect result without it?

Here's the code:

public class VolatileTest implements Runnable {

    private int count;
    private volatile boolean stopped;

    @Override
    public void run() {
        while (!stopped) {
            count++;
        }
        System.out.println("Count 1 = " + count);
    }

    public void stopCounting() {
        stopped = true;
    }

    public int getCount() {
        if (!stopped) {
            throw new IllegalStateException("not stopped yet.");
        }
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileTest vt = new VolatileTest();
        Thread t = new Thread(vt);
        t.start();
        Thread.sleep(1000L);
        vt.stopCounting();
        System.out.println("Count 2 = " + vt.getCount());
    }
}

6 Answers 6

10

Victor is right, there are issues with your code: atomicity and visibility.

Here's my edition:

    private int count;
    private volatile boolean stop;
    private volatile boolean stopped;

    @Override
    public void run() {
        while (!stop) {
            count++; // the work
        }
        stopped = true;
        System.out.println("Count 1 = " + count);
    }

    public void stopCounting() {
        stop = true;
        while(!stopped)
           ; //busy wait; ok in this example
    }

    public int getCount() {
        if (!stopped) {
            throw new IllegalStateException("not stopped yet.");
        }
        return count;
    }

}

If a thread observes that stopped==true, it's guaranteed that the work completes and the result is visible.

There is a happens-before relation from volatile write to volatile read (on the same variable), so if there are two threads

   thread 1              thread 2

   action A
       |
 volatile write  
                  \
                     volatile read
                          |  
                       action B

action A happens-before action B; writes in A are visible by B.

Sign up to request clarification or add additional context in comments.

2 Comments

As is, the example is broken, because count++ is not an atomic operation. You must either acquire a lock around that operation, or use a non-blocking AtomicInteger.
(unless, of course, you make sure that only 1 thread will ever call the method run() -- anyways, the code above cannot be claimed to be thread-safe)
1

It was always hard to me to illustrate concurrency problems in a convincing way: well, fine, it's all nice about happens-before and stuff, but why should one care? Is there a real problem? There are lots and lots of poorly written, poorly synchronized programs—and they still work most of the time.

I used to find a resort in a "works most of the time VS works" rhetoric—but, frankly, it's a weak approach. So what I needed is an example which would make difference obvious—and, preferably, painful.

So here is a version which actually does show the difference:

public class VolatileExample implements Runnable {
    public static boolean flag = true; // do not try this at home

    public void run() {
        long i = 0;
        while (flag) {
            if (i++ % 10000000000L == 0)
                System.out.println("Waiting  " + System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new VolatileExample());
        thread.start();
        Thread.sleep(10000L);
        flag = false;
        long start = System.currentTimeMillis();
        System.out.println("stopping " + start);
        thread.join();
        long end = System.currentTimeMillis();
        System.out.println("stopped  " + end);
        System.out.println("Delay: " + ((end - start) / 1000L));
    }
}

A simple run shows:

Waiting  1319229217263
stopping 1319229227263
Waiting  1319229242728
stopped  1319229242728
Delay: 15

That is, it takes more than ten seconds (15 here) for a running thread to notice there was any change.

With volatile, you have:

Waiting  1319229288280
stopping 1319229298281
stopped  1319229298281
Delay: 0

that is, exiting (almost) immediately. The resolution of currentTimeMillis is around 10ms, so the difference is more that 1000 times.

Note it was Apple's version of (ex-)Sun JDK, with -server option. The 10-second wait was added in order to let JIT compiler find out that the loop is hot enough, and optimize it.

Hope that helps.

Comments

1

Simplifying @Elf example further, where the other thread will never get the value which was updated by other thread. Removing System.out.println as there is synchronized code inside println and out is static, somehow that helps the other thread to get the latest value of flag variable.

public class VolatileExample implements Runnable {
   public static boolean flag = true; 


  public void run() {
     while (flag);
  }

  public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new VolatileExample());
    thread.start();
    Thread.sleep(1000L);
    flag = false;
    thread.join();
  }
}

Comments

0

To illustrate the importance of the volatile keyword when it comes to concurrency, all you need to do is make sure that the volatile field is modified and read in a separate threads.

Comments

0

UPDATE My answer is wrong, see answer from irreputable.


It's not thread-safe, since access to count is not there's only one writer thread. Should there be another writer thread, value of count would be become inconsistent to the number of updates.

Visibility of count value to main thread is ensured by checking stopped volatile inside getCount method. This is what is called piggybacking on synchronization in Concurrency in practice book.

13 Comments

int updates are atomic, as per the JLS. However, read/write ordering might be problematic without volatile, when the variable isn't fenced by synchronisation.
The idea of the exception in getCount is to guarantee that count is only accessed by an external thread once the writes to count are finished. And I thought writing to the volatile variable guaranteed the visibility of count to all the threads. Isn't it right?
@JBNizet, Why would you think it's alright to modify a field in one thread and expect to get the correct value when reading it from another thread without synchronization?
Update may be, but code is using ++, which is read-change-store operation.
Thank you all for answering. I knew I would miss something. I'm just glad I'm not the only one :-)
|
0

Wrong code with which we cannot assume x = 1 also if y is already 2:

Class Reordering {
  int x = 0, y = 0;
  public void writer() {
    x = 1;
    y = 2;
  }

  public void reader() {
    int r1 = y;
    int r2 = x;
  }
}

Example of use of volatile keyword:

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}

Source: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

Comments

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.