Jean-Francois's solution is the correct one:  you absolutely must have some sort of synchronization when threads access a shared variable, whether it's via volatile, synchronized, etc.  
I would also add that your while loop amounts to what is called busy waiting - that is, repeatedly testing a condition in a concurrent setting.  The tight loop of the busy waiting in this code may hog the CPU.  At the very least its effects on system resources will be unpredictable. 
You may want to explore the condition variable approach to dealing with multiple threads being affected by a single shared condition. Java has many higher level tools for this in the java.util.concurrent library, but it's good to know the older lower-level API methods, especially since you are working directly with Thread instances already.
Every Object has wait() and notifyAll() methods. The Object represents the condition, or at least the monitor associated with it.  The wait() method is called within a while loop that tests the condition, and blocks the calling thread until some other thread calls notifyAll().  Then all waiting threads will be awakened, and they will all have compete for the lock and the chance to test the condition again. If the condition just stays true at that point, then all the threads will proceed.
Here's what your code would look like using this approach:
public class GuardedBlock {
    private boolean guard = false;
    private static void threadMessage(String message) {
        System.out.println(Thread.currentThread().getName() + ": " + message);
    }
    public static void main(String[] args) throws Exception {
        GuardedBlock guardedBlock = new GuardedBlock();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    synchronized (guardedBlock) {
                        guardedBlock.guard = true;
                        guardedBlock.notifyAll();
                    }
                    threadMessage("Set guard=true");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                threadMessage("Start waiting");
                while (!guardedBlock.guard) {
                    synchronized (guardedBlock) {
                        try {
                            guardedBlock.wait();
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                threadMessage("Finally!");
            }
        });
        thread1.start();
        thread2.start();
        thread2.join();
        System.out.println("Done");
    }
}
Notice the following:
- Lock must be taken whenever condition is read or written (via
synchronizedkeyword.)
- guardcondition is tested inside a- whileloop, but that loop blocks during the- wait()call. The only reason it is still a- whileloop is to handle situations where there are many threads and the condition changes many times. Then the condition should be re-tested when a thread is awakened, in case another thread changes the condition in the tiny gap between the awakening and the re-taking of the lock.
- Waiting threads are notified when guardcondition is set totrue(vianotifyAll()calls.)
- Program blocks on the thread2instance at the
very end, so that we don't exit the main thread before all threads
finish (via thejoin()call.)
If you look at the Object API, you'll see there's also a notify() method. It's simpler to always use notifyAll(), but if you want to understand the difference between these two methods, see this SO post.