9

I am working on someone's code and came across the equivalent of this:

for (int i = 0; i < someVolatileMember; i++) {
    // Removed for SO
}

Where someVolatileMember is defined like this:

private volatile int someVolatileMember;

If some thread, A, is running the for loop and another thread, B, writes to someVolatileMember then I assume the number of iterations to do would change while thread A is running the loop which is not great. I assume this would fix it:

final int someLocalVar = someVolatileMember;
for (int i = 0; i < someLocalVar; i++) {
    // Removed for SO
}

My questions are:

  • Just to confirm that the number of iterations thread A does can be changed while the for loop is active if thread B modifies someVolatileMember
  • That the local non-volatile copy is sufficient to make sure that when thread A runs the loop thread B cannot change the number of iterations
3
  • 4
    Yes and yes.... Commented Oct 10, 2018 at 15:35
  • 1
    Thanks, just accustomed to C++ more so than Java Commented Oct 10, 2018 at 15:39
  • No problem, I just had to add the dots on the end cause the comment wasn't long enough... Commented Oct 10, 2018 at 15:47

3 Answers 3

5

Your understanding is correct:

  1. Per the Java Language Specification, the semantics of a volatile field ensure consistency between values seen after updates done between different threads:

    The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.

    A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).

    Note that even without the volatile modifier, the loop count is likely to change depending on many factors.

  2. Once a final variable is assigned, its value is never changed so the loop count will not change.

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

3 Comments

Can you explain this, "Note that even without the volatile modifier, the loop count is likely to change depending on many factors"
@asimes With a private int someMember, if thread B changes someMember, thread A will see the updated value at some point. volatile just ensures that it will be seen immediately.
@manouti theoretically it might never see this updated value and immediately is far fetched, once a volatile is written, caches containing it are invalidated and that takes time too; thus the JLS says "volatile writes happen before subsequent volatile reads"
0

Well first of all that field is private (unless you omitted some methods that actually might alter it)...

That loop is a bit on non-sense, the way it is written and assuming there are methods that actually might alter someVolatileMember; it is so because you might never know when if finishes, or if does at all. That might even turn out to be a much more expensive loop as having a non-volatile field, because volatile means invalidating caches and draining buffers at the CPU level much more often than usual variables.

Your solution to first read a volatile and use that is actually a very common pattern; it's also given birth to a very common anti-pattern too : "check then act"... You read it into a local variable because if it later changes, you don't care - you are working with the freshest copy you had at the moment. So yes, your solution to copy it locally is fine.

3 Comments

Private has nothing to do with threads, a single class could have two threads. At the very beginning of my question I wrote that I did not write the code
@asimes I wan't criticizing your code, but saying that in general (edited to drop the confusion), if that field is private - how would some two threads alter it? except via some methods?
@asimes and you probably really meant is : a single instance that has some invariants (your volatile property) can be accessed by two threads. If so, yes - you are correct, but now that this field is private - how would those two threads write to it?
0

There are also performance implications, since the value of volatile is never fetched from the most local cache but additional steps are being taken by the CPU to ensure that modifications are propagated (it could be cache coherence protocols, deferring reads to L3 cache, or reading from RAM). There are also implications to other variables in scope where volatile variable is used (these get synced with main memory too, however i am not demonstrating it here).

Regarding performance, following code:

private static volatile int limit = 1_000_000_000;

public static void main(String[] args) {
    long start = System.nanoTime();
    for (int i = 0; i < limit; i++ ) {
        limit--;   //modifying and reading, otherwise compiler will optimise volatile out 
    }
    System.out.println(limit + " took " + (System.nanoTime() - start) / 1_000_000 + "ms");
}

... prints 500000000 took 4384ms

Removing volatile keyword from above will result in output 500000000 took 275ms.

2 Comments

first there is rarely "fetched" from main memory as such - there is cache coherency protocol for that; and second you are doing volatile reads and volatile writes; unlike the OP example
@Eugene - thanks for pointing to cache coherence. I will update the answer. Regardless of what is the CPU architecture, presence of volatile with platform using caches means that additional steps are taken to fulfill all guarantees of volatile. Also I am writing to that volatile to showcase the worst case, where compiler cannot optimise the code by removing volatile if the value was only read.

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.