In general each thread has its own copy of a variable stored in a CPU cache after being read from main memory. Each thread is not concerned with the value of the same variable in the other thread.
volatile keyword allows to read and write the variable directly from the main memory and not the thread local stack.
Consider a scenario in which a count variable is holding the number of times a method is called for a given class irrespective of n number of threads present, in this case, irrespective of thread access the count has to be increased and value needs to be consistent across all the threads. In this case the count variable is declared as volatile. Let's name the variable
counter. When ThreadA comes and increment the value
counter = 1. Immediately the value will be written to main memory. Now ThreadB comes and increment the same variable and now
counter=2. Although this looks great in theory but this might not happen.
counter=0. ThreadA and ThreadB arrives at the same time and access the counter. Both will see a value
0. Both the threads will increment it set the value to
1 in main memory. Read and write of volatile variables becomes tricky if the thread is not checking the value after read. The above problem can be solved using synchronize keyword.
Even if the volatile keyword guarantees that all reads of a volatile variable are read directly from main memory, and all writes to a volatile variable are written directly to main memory, there are still situations where it is not enough to declare a variable volatile. threads could write to the variable correctly without checking the previous values. Volatile keyword can be used for situtations where one thread is writing the value and others are reading the latest values.