What is the "volatile" keyword used for?
Asked Answered
A

8

139

I read some articles about the volatile keyword but I could not figure out its correct usage. Could you please tell me what it should be used for in C# and in Java?

Akkadian answered 7/8, 2010 at 14:19 Comment(2)
One of the problems with volatile is that it means more than one thing. It being information to the compiler not to do funky optimizations is a C legacy. It also means that memory barriers should be used on access. But in most cases it just costs performance and / or confuses people. :PGuevara
Related: Simplest and understandable example of volatile keyword in Java and Volatile vs Static in JavaTrinitroglycerin
M
106

For both C# and Java, "volatile" tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself. The compiler will then avoid any optimisations that may result in problems if the variable changes "outside of its control".

Moulmein answered 7/8, 2010 at 14:27 Comment(0)
R
177

Consider this example:

int i = 5;
System.out.println(i);

The compiler may optimize this to just print 5, like this:

System.out.println(5);

However, if there is another thread which can change i, this is the wrong behaviour. If another thread changes i to be 6, the optimized version will still print 5.

The volatile keyword prevents such optimization and caching, and thus is useful when a variable can be changed by another thread.

Robichaud answered 7/8, 2010 at 14:26 Comment(6)
I believe the optimisation would still be valid with i marked as volatile. In Java it is all about happens-before relationships.Augustina
Thanks for posting, so somehow volatile has connections with variable locking ?Akkadian
@Mircea: That is what I was told that marking something as volatile was all about: marking a field as volatile would use some internal mechanism to allow threads to see a consistent value for the given variable, but this is not mentioned in the answer above... maybe someone can confirm this or not? ThanksAzevedo
@Sjoerd: I'm not sure I understand this example. If i is a local variable, no other thread can change it anyway. If it's a field, the compiler can't optimize the call unless it's final. I don't think the compiler can make optimizations based on assuming that a field "looks" final when it's not explicitly declared as such.Rondi
@poly: Yes, it would probably have helped if that local variable were passed by reference to a thread.Courbet
C# and java are not C++. This is not correct. It does not prevent caching and it does not prevent optimization. It is about read-acquire, and store-release semantics, which are required on weakly ordered memory architectures. It is about speculative execution.Claudine
M
106

For both C# and Java, "volatile" tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself. The compiler will then avoid any optimisations that may result in problems if the variable changes "outside of its control".

Moulmein answered 7/8, 2010 at 14:27 Comment(0)
C
45

Reads of volatile fields have acquire semantics. This means that it is guaranteed that the memory read from the volatile variable will occur before any following memory reads. It blocks the compiler from doing the reordering, and if the hardware requires it (weakly ordered CPU), it will use a special instruction to make the hardware flush any reads that occur after the volatile read but were speculatively started early, or the CPU could prevent them from being issued early in the first place, by preventing any speculative load from occurring between the issue of the load acquire and its retirement.

Writes of volatile fields have release semantics. This means that it is guaranteed that any memory writes to the volatile variable are guaranteed to be delayed until all previous memory writes are visible to other processors.

Consider the following example:

something.foo = new Thing();

If foo is a member variable in a class, and other CPUs have access to the object instance referred to by something, they might see the value foo change before the memory writes in the Thing constructor are globally visible! This is what "weakly ordered memory" means. This could occur even if the compiler has all of the stores in the constructor before the store to foo. If foo is volatile then the store to foo will have release semantics, and the hardware guarantees that all of the writes before the write to foo are visible to other processors before allowing the write to foo to occur.

How is it possible for the writes to foo to be reordered so badly? If the cache line holding foo is in the cache, and the stores in the constructor missed the cache, then it is possible for the store to complete much sooner than the writes to the cache misses.

The (awful) Itanium architecture from Intel had weakly ordered memory. The processor used in the original XBox 360 had weakly ordered memory. Many ARM processors, including the very popular ARMv7-A have weakly ordered memory.

Developers often don't see these data races because things like locks will do a full memory barrier, essentially the same thing as acquire and release semantics at the same time. No loads inside the lock can be speculatively executed before the lock is acquired, they are delayed until the lock is acquired. No stores can be delayed across a lock release, the instruction that releases the lock is delayed until all of the writes done inside the lock are globally visible.

A more complete example is the "Double-checked locking" pattern. The purpose of this pattern is to avoid having to always acquire a lock in order to lazy initialize an object.

Snagged from Wikipedia:

public class MySingleton {
    private static object myLock = new object();
    private static volatile MySingleton mySingleton = null;

    private MySingleton() {
    }

    public static MySingleton GetInstance() {
        if (mySingleton == null) { // 1st check
            lock (myLock) {
                if (mySingleton == null) { // 2nd (double) check
                    mySingleton = new MySingleton();
                    // Write-release semantics are implicitly handled by marking
                    // mySingleton with 'volatile', which inserts the necessary memory
                    // barriers between the constructor call and the write to mySingleton.
                    // The barriers created by the lock are not sufficient because
                    // the object is made visible before the lock is released.
                }
            }
        }
        // The barriers created by the lock are not sufficient because not all threads
        // will acquire the lock. A fence for read-acquire semantics is needed between
        // the test of mySingleton (above) and the use of its contents. This fence
        // is automatically inserted because mySingleton is marked as 'volatile'.
        return mySingleton;
    }
}

In this example, the stores in the MySingleton constructor might not be visible to other processors before the store to mySingleton. If that happens, the other threads that peek at mySingleton will not acquire a lock and they will not necessarily pick up the writes to the constructor.

volatile never prevents caching. What it does is guarantee the order in which other processors "see" writes. A store release will delay a store until all pending writes are complete and a bus cycle has been issued telling other processors to discard/writeback their cache line if they happen to have the relevant lines cached. A load acquire will flush any speculated reads, ensuring that they won't be stale values from the past.

Claudine answered 7/10, 2016 at 2:50 Comment(3)
Good explanation. Also good double-check locking example. However, I'm still unsure about when to use as I'm worried about the caching aspects. If I write a queue implementation where only 1 thread will be writing and only 1 thread will be reading, can I get by without locks and just mark my head and tail "pointers" as volatile? I want to ensure that both the reader and writer see the most up to date values.Dimpledimwit
Both head and tail need to be volatile to prevent the producer from assuming tail won't change, and to prevent the consumer from assuming head won't change. Also, head must be volatile to ensure that the queue data writes are globally visible before the store to head is globally visible.Claudine
+1, Terms like latest / "most updated" unfortunately imply a concept of the singular correct value. In reality two competitors can cross a finish line at the exact same time - on a cpu two cores can request a write at the exact same time. After all, cores don't take turns doing work - that would make multi-core pointless. Good multi-thread thinking / design shouldn't focus on trying to force low-level "latestness" - inherently fake since a lock just force cores to arbitrarily select one speaker at a time w/o fairness - but rather try to design away the need for such an unnatural concept.Guevara
P
39

To understand what volatile does to a variable, it's important to understand what happens when the variable is not volatile.

  • Variable is Non-volatile

When two threads A & B are accessing a non-volatile variable, each thread will maintain a local copy of the variable in it's local cache. Any changes done by thread A in it's local cache won't be visible to the thread B.

  • Variable is volatile

When variables are declared volatile it essentially means that threads should not cache such a variable or in other words threads should not trust the values of these variables unless they are directly read from the main memory.

So, when to make a variable volatile?

When you have a variable which can be accessed by many threads and you want every thread to get the latest updated value of that variable even if the value is updated by any other thread/process/outside of the program.

Pother answered 7/3, 2013 at 14:31 Comment(2)
Wrong. It has nothing to do with "preventing caching". It is about reordering, by the compiler, OR the CPU hardware through speculative execution.Claudine
I don't think this is correct at all. If it was correct then multithreaded code would require volatile all the time.Allhallowmas
H
35

The volatile keyword has different meanings in both Java and C#.

Java

From the Java Language Spec :

A field may be declared volatile, in which case the Java memory model ensures that all threads see a consistent value for the variable.

C#

From the C# Reference (retrieved 2021-03-31):

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. The compiler, the runtime system, and even hardware may rearrange reads and writes to memory locations for performance reasons. Fields that are declared volatile are not subject to these optimizations. (...)

Hinkley answered 7/8, 2010 at 14:37 Comment(5)
Thank you very much for posting, as i understood in Java it acts like locking that variable in a thread context, and in C# if used the value of variable can be changed not only from program , external factors such as OS can modify its value (no locking implied)... Please let me know if i understood right those differences...Akkadian
@Akkadian in Java there is no locking involved, it just ensures that the most up to date value of the volatile variable will be used.Hinkley
Does Java promise some sort of memory barrier, or is it like C++ and C# in only promising not to optimize the reference away?Courbet
The memory barrier is an implementation detail. What Java actually promises is that all reads will see the value written by the most recent write.Yamen
@StevenSudit Yes, if the hardware requires a barrier or load/acquire or store/release then it will use those instructions. See my answer.Claudine
K
9

In Java, "volatile" is used to tell the JVM that the variable may be used by multiple threads at the same time, so certain common optimizations cannot be applied.

Notably the situation where the two threads accessing the same variable are running on separate CPU's in the same machine. It is very common for CPU's to cache aggressively the data it holds because memory access is very much slower than cache access. This means that if the data is updated in CPU1 it must immediately go through all caches and to main memory instead of when the cache decides to clear itself, so that CPU2 can see the updated value (again by disregarding all caches on the way).

Kt answered 7/8, 2010 at 14:50 Comment(0)
B
0

When you are reading data that is non-volatile, the executing thread may or may not always get the updated value. But if the object is volatile, the thread always gets the most up-to-date value.

Bastien answered 29/3, 2018 at 5:24 Comment(2)
Can you rephrase your answer?Nonchalance
volatile keyword will give you most updated value rather than cached value.Bastien
H
-2

Volatile is solving concurrency problem. To make that value in sync. This keyword is mostly use in a threading. When multiple thread updating same variable.

Handicapper answered 25/4, 2020 at 19:54 Comment(1)
I don't think it "solves" the problem. It's a tool that help in some circumstance. Don't rely on volatile for situations where a lock is needed, as in a race condition.Tarrasa

© 2022 - 2024 — McMap. All rights reserved.