when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change
Let's say you have the following class:
public class Shared {
public int a;
public int b;
public volatile int c;
}
Now let's say that thread A has a reference to an instance of this class and does
shared.a = 1;
shared.b = 2;
shared.c = 3;
And let's say that thread B has a reference to the same instance and does
display(c);
display(b);
display(a);
Then, if the value displayed for c is 3 (i.e. if the write of thread A has happened before the read of thread B), then it's guaranteed by the Java memory model that 2 and 1 will also be displayed for b and a respectively, because all the actions of thread A that have been made prior to the write to the volatile c are guaranteed to be visible by a thread that has read the new value of c.