volatile también se utiliza si la variable está siendo "compartida" en más de una hebra, por ejemplo.
Es necesaria para que el compilador no realice optimizaciones sobre su uso. Siempre que sea posible, el compilador leerá el valor de la variable y la meterá en un registro del procesador. Todos los usos de la variable (lectura o escritura) que se hagan posteriormente (mientras la variable se mantenga en el registro) no tendrán su repercusión en memoria, pues el compilador meterá instrucciones en código ensamblador para utilizar el registro de la CPU.
Si la variable es volátil (puede cambiar por una razón externa a la ejecución secuencial del programa), el uso del registro del procesador con esa variable es incorrecta, pues nunca se verán los cambios realizados en la copia en memoria. El "volatile" evita, por lo tanto, que el compilador use registros para esa variable. De algún modo, es la antítesis de "register".
Suerte!