Jean-Francois solution is the right one: you absolutely must have some kind of synchronization when the threads access a shared variable, whether through volatile , synchronized , etc.
I would also add that your while is equal to what is called waiting for a wait - that is, by re-testing the condition in a concurrent setting. The close loop of busy waiting in this code may be processor dependent. At least its impact on system resources will be unpredictable.
You might want to explore a conditional variable approach to working with multiple threads affected by one general condition. Java has many higher-level tools for this in java.util.concurrent , but it's good to know the old lower-level API methods, especially since you work directly with Thread instances.
Each Object has wait() and notifyAll() . Object represents a condition, or at least the monitor associated with it. The wait() method is called in a while , which checks the condition and blocks the calling thread until some other thread calls notifyAll() . Then all waiting threads will be woken up, and they will all compete for blocking and the ability to check the status again. If the condition remains true at this point, all flows will continue.
Here's what your code would look like with 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"); } }
Please note the following:
- A lock should be executed whenever a condition is read or written (via
synchronized .) guard condition is tested inside the while , but this loop is blocked during the wait() call. The only reason it is still a while tag is to handle situations where many threads and state change many times. Then, the condition must be re-checked when the thread wakes up, if another thread changes the condition in the tiny gap between waking up and re-locking the lock.- Waiting threads are notified when the
guard condition is set to true (via calls to notifyAll() .) - The program blocks in the
thread2 instance thread2 at the very end so that we do not exit the main thread until all threads (via a join() call.)
If you look at the Object API, you will also see the notify() method. The easiest way is to use notifyAll() , but if you want to understand the difference between the two methods, see this SO post. .
source share