Controlling Threads

Controlling threads is the art of moving them from one state to another. You control threads by triggering state transitions.

Starting a thread

public void start()

Once a Thread has been created it is started by a call to the Thread start() method. The start() method puts the thread into the ready state.
The scheduler decides when to transfer it to the running state.

 

Stopping a thread

A good programming practice is to let the thread die naturally, for example, by letting its run() method return (so, you should design your run method appropriately). Another option -- by setting the reference to the thread to null and letting the garbage collector take care of it.

The following example relies on currentThread() method:

public static Thread currentThread()
     Returns a reference to the currently executing thread object.

Mono-threaded version of text area (here is the source code) and the 2-threaded fix (source code).

Pathways out of the running state

Note the wording "pathways out of" the running state.

After these transitions, the thread goes to the Ready state, not back to the Running state.
In other words, the thread scheduler decides when a thread which left a Running state will return to that state.

Yielding

public static native void Thread.yield() throws InterruptedException

Yielding causes the currently running thread to move to the ready state. Thus other threads are given a chance to enter the Running state. (Actually, the scheduler could promptly return the thread that has just yielded back to the running state!)

Scheduling implementations: UNIX/Solaris and NT.

Solaris has what is called preemptive scheduling.

In this case a thread can only be forced out of a running state

Windows NT has what is called time slicing

Sometimes called round robin scheduling. Each thread is given a maximum execution time after which it must yield to another thread and then wait for its turn to come up again. In these systems, Java's yield() method is really not necessary.

Yielding appropriately is especially important on UNIX systems such as Solaris which do not have time slicing. On time slicing systems such as MS Windows NT, the operating system will kick greedy threads off the CPU periodically anyway.

Because you do not know on what machine your program (or your applet) will run, you cannot make assumptions what implementation of scheduling will be used.
In other words, you need to write your multi-threaded program in a way that it will function correctly on any platform (Solaris, Windows NT, etc).

Sleeping

public static void sleep(int ms) throws InterruptedException

(There are other versions, see documentation)

Causes the currently running thread to cease using the CPU for the specified number of milliseconds. The thread goes to the sleeping state. After approximately the specified time, the thread is put in the ready state and gets to the running state only at the mercy of the scheduler. Short sleeps are very helpful for slowing things down.

In particular, because multiple repaint() requests can unintentionally be combined into one, it is possible to slow down repaint() requests using sleep(). Note that sleep() is a static method.

Mono-threaded version of 9 circles (here is the source code) and the 2-threaded fix with sleep() (sourcecode.)

Blocking

A thread can block waiting for something to happen. The most common situation concerns some kind of input. For example,

Unless jupiter has produced a byte on that port the read() just blocks until the byte appears. Once the byte appears and is read, the thread's state is moved to ready and it is up to the scheduler to put it in the running state at which point the thread can continue with processing the byte.

Waiting

This state is somewhat different from the rest (it will be discussed later).

 

More about Priorities

See   On-line Tutorial: it includes a brief discussion of time-slicing and scheduling.

CAUTION: Some platforms (e.g., Windows NT) have fewer priority levels than 10 (specified by Java). On those platforms, no matter what mapping of priority levels is chosen, some of the 10 JVM levels will be mapped to the same platform levels. Whenever the host platform uses fewer priority levels than Java specifies, a thread can be preempted by another thread with a seemingly lower priority.

In the SUN JVM for Linux, thread priorities are ignored altogether. That means that you cannot rely on priority levels in your multithreaded programs.


 

Examples

  1. Clock applet (here is the Source code)

    The discussion of transitions between thread states in the Java Tutorial: The Life Cycle of a Thread.

     

  2. An animation of a bouncing circle (here is a source code)

     

  3. Threads demo: the 1st thread is defined by subclassing the Thread class, while the 2nd implements the Runnable interface and passes a Runnable object to the Thread() constructor. This example also demonstrates how you might use the methods sleep(), yield(), join(). This program generates this output.

Documentation of the ThreadLocal class.