CS136, Lecture 34

  1. Threads
    1. Thread life-cycle
    2. Monitors and synchronized methods

Threads

Java supports programming concurrent processes with threads. Several threads (processes) can be running on computer systems. If the system has only a single processor then the threads take turns running, with one of the highest priority threads always running. If there are the same number of processors as threads then each can be assigned to a single processor. In general, though, there are more processes active than there are processors.

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 or by declaring a class to implement the Runnable specification. We'll focus here on defining a subclass of Thread.

Thread life-cycle

The life-cycle of a thread is as follows: When a thread is created it is in the "born" state. Nothing happens until the thread's "start" method is called. It then enters the "ready" state, which means it is waiting to be assigned to a processor. When it becomes one of the highest priority threads which is ready, it can be "dispatched" or assigned to a processor. It then generally runs until it is swapped out for another thread (going back to the ready state).

Other things can happen to a thread while it is running:

If it runs to completion or a "stop" message is sent to it then it moves to a "dead" state.

If it issues an I/O request it moves to a blocked state until the I/O request is completed, and then moves to the "ready" state again.

If a "sleep(n)" message is sent to it, then it waits for at least n milliseconds, and then returns to the ready state. Used when want fixed delay time.

If a "suspend" message is sent to it, then it stays in the "suspended" state until a resume message is sent to it. Good if you know which thread to restart.

If a wait message is sent to it, then it goes into the "waiting" state where it waits in a queue. The first item in the wait queue goes into the "ready" state on a call to notify by another thread associated with the object. A call of notifyAll puts all of the threads on the wait queue in the "ready" state. Used when you don't know who is waiting to proceed. Wakes up whatever thread has been waiting longest.

Can ask thread if it is still alive with isAlive() method.

Note sometimes it is hard to suspend a thread if it is busy. As a result, often have method which sets a flag which can be checked each time through loop to see if want to suspend. See Concurrent Balls example.

In lab Wednesday, will create a timer which can be interrupted and reset.

Monitors and synchronized methods

More complicated if both need to access the same data.

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 synchronized methods can be active at a time. (Ordinarily, several methods may be executing methods of the object 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.