1) "volatile" has nothing to do with multithreading. It is not atomic.
2) The compiler will not reorder the sequence of "volatile" variables, but may reorder the sequence of "volatile" and non-volatile. CPU may reorder the execution of "volatile" variables (CPU memory order, depending on the CPU vendor).
3) In a single threaded env, "volatile int vi = 0", vi = 1; int read_back = vi; assert (read_back == 1); the assertion may fail. WHY ? (think of "vi" is mapping to an hardware port. Memory mapped I/O).
4) The compiler will not do any optimization for volatile variables. It generates code which always read the value from the memory.
5) "volatile" doesn't guarantee Acquire-Release semantics. "Acquire": read an atomic, the code after the "read of an atomic" will NOT be reordered before the read. "Release" : write an atomic, the code before the "write of an atomic" will NOT be reordered after the write (everything will be committed to memory, and seen by other CPUs).