Your java programs always have a number of threads going concurrently. For example, there is a thread for your main program, and another thread for the garbage collector. Other threads may be taking care of operating systems needs, graphics, etc.
New threads of execution can be created by defining a subclass of the class Thread and overriding the run method or by declaring a class to implement the Runnable interface. We'll focus here on defining a subclass of Thread.
Not surprisingly, Thread implements Runnable.
It then generally runs (by executing the run method) until it is swapped out for another thread (going back to the ready state) or until one of the following happens:
Can ask thread if it is still alive with isAlive() method.
It is hard to suspend a thread if it is busy. As a result, often have a method that sets a flag that can then be checked each time through the loop to see if want to suspend. The thread can then suspend itself, though it will be "resumed" from outside. (A good implementation of the StopWatch -- to be done in lab today -- might proceed in this way.)
Can set up what is often called a monitor: Data structure which only allows access by one thread at a time.
Can declare methods in a class to be synchronized. Restricts so only one of the synchronized methods can be active at a time. (Ordinarily, several methods of the object may be executing at the same time!)
When a synchronized method is running, a "lock" is established on the synchronized methods, so that none can be executed until the lock is released by the executing thread.
Use pair of methods wait and notify when must wait on a condition. wait releases lock and places thread on a queue waiting to be notified that condition might have changed.
wait and notify can only be invoked from within synchronized methods (or methods called from within synchronized methods).
Use notifyAll if not sure which object is first in queue.
See code for circular buffer in Buffer folder in the Class examples on Cider Press.