volatile java & c++
Comparing the volatile keyword in Java, C and C++
Declaring a volatile variable in Java
In Java, you can only add the volatile keyword to class members:
public class MyClass {
private volatile int count = 0;
public static volatile int sum = 0;
}
Arrays can be volatile, but the elements won’t be volatile:
volatile int[] numbers = new int[10];
...
numbers[0] = 5; // not volatile
Local variables cannot be volatile:
public void doStuff() {
volatile int count = 0; // Error: illegal start of expression
}
Declaring a volatile variable in C and C++
In C and C++, the volatile keyword can be applied to local and global variables:
volatile int globalNumber = 0;
int* volatile pInt; // volatile pointer to nonvolatile int
volatile int* volatile pInt2; // volatile pointer to a volatile int
void doStuff() {
volatile float localNumber = 5.0f;
}
structs can also have volatile fields:
struct A {
volatile double n = 0.0;
};
In C++, class members can also be volatile:
class T {
volatile long id = 0;
};
In Java, volatile means memory visibility
When a variable is declared volatile in Java, the compiler is informed that such variable is shared and its value can be changed by another thread. Since the value of a volatile variable can change unexpectedly, the compiler has to make sure its value is propagated predictably to all other threads.
volatile boolean isDone = false;
public void run() {
while (!isDone)
doWork();
}
The isDone variable is declared volatile. If that variable is set to true by another thread, this piece of code will read the new value and immediately leave the while-loop. If we forget to add the volatile modifier to that variable, there is a good chance the loop will never finish if the variable is set to true by another thread.
If a variable is declared volatile, all threads must read its most recent written value.
In Java, the volatile keyword guarantees memory visibility and it is meant to be used in concurrent programming.
In C/C++, volatile means no optimizations
-O0 = no optimization
-O1 = somewhere between -O0 and -O2
-O2 = Moderate level of optimization, which enables most optimizations
-O3 = Like -O2 with extra optimizations to reduce code size
You may ask why would someone use this kind of less efficient code. One answer is memory-mapped I/O.
In C/C++, the volatile keyword is NOT suitable for concurrent programming. It is meant to be used only with special memory (e.g., memory-mapped I/O).
In Java, volatile doesn't guarantee atomicity and thread-safety
A common misunderstanding about the volatile keyword in Java is whether operations on such variables are atomic or not. The answer to this question can be YES and NO.
Operations are atomic if you look at individual reads and writes of the volatile variable.
Operations are NOT atomic if the code has to read the value before updating it.
volatile int i = 0;
i += 10; // not atomic
AtomicInteger i = new AtomicInteger(0);
i.addAndGet(10); // atomic
In C/C++, volatile can be used in return types and parameters
volatile int work(volatile int x) {
return x + 1;
}
In C++, volatile can also be used in class methods, constructors and operators
class MyClass {
public:
int normalMethod() {
return 1;
}
int volatileMethod() volatile {
return 2;
}
};
If an instance of this class is declared volatile, then the code can only call volatile methods on that object:
int main() {
volatile MyClass a;
a.normalMethod(); // error: nonvolatile method cannot be called
a.volatileMethod(); // OK
return 0;
}
Conclusion
In Java, volatile variables are meant to be used in multithreaded contexts. Threads that concurrently read a volatile variable will always read its latest value, which means the JVM will not cache the variable internally and its value will be shared across all processors.
Atomicity and thread-safety in Java are not guaranteed by volatile variables. In summary, you should use the volatile keyword if writes to the variable do not depend on its current value, or you can ensure that only a single thread ever updates the value.
In C/C++, the volatile keyword is meant to be used with special memory: manipulating I/O registers or memory-mapped hardware. All optimizations on those variables will be disabled, which means that assignments and their order will be preserved. The volatile qualifier doesn’t help us in multithreaded code.
Java | C++ | |
---|---|---|
Purpose | Concurrent Programming | Special Memory (e.g., memory mapped I/O) |
What difference does it make? | Adds visibility to the variable | Disables optimizations on the variable |
Is the operation atomic? | Yes for individual reads and writes; No if the write depends on the current value; |
No |
Applies to | class fields | local and global variables; return types; function parameters; class fields and methods (C++ only); |