Thread Programming

Notes for CSC 120 Programming I
Burton Rosenberg
November 2, 2000

Threads are a difficult concept. Previous to Java, threads were never part of the mainstream language. C and C++ handled them as libraries. With graphics-oriented programming and events, threads are very important. Java including thread primitives in its standard library and integrating thread support in the language (the synchronized construct) is a major development in programming languages.

What is a thread?

A thread is a context and its associated flow-of-control. We picture our programs are being run step-by-step, with control passed according to the flow control constructs of the language. By default, flow proceeds from one statement to the next. The built-in if and looping constructs modify this flow but in a limited, continuous, fashion. As the programs develops, the variables and their values are understood to be unchanged from one step to the next, unless an assignment is performed to a particular variable.

But a computer must handle multiple objectives are the same time. It must run several person's programs. Or even if the computer is dedicated to one user, it must handle multiple tasks the user implicitly undertakes: such as responding to mouse movements (one thread) while creating a text file in a text processor (another thread).

In reality, the computer achieves this by switching rapidly from one thread to another, advancing each thread's computation by a little, so that threads appear to be advancing simultaneously. To do this, the computer does a context switch in which the environment of variables and program pointers are stored in a control block. The state of the thread is "frozen". Threads can be brought to life by unstoring the control block, placing it back on the processor, and continuing the program in the state it was left off. A computer with multiple processors can indeed have multiple threads truely running simultaneously, with one thread per processor. But even these machines will multithread each of the multiple processors.

So a thread is a context, that is, the view of the environment that a single program has control over and cares about, and the ability to walk through the program, obeying program statements and altering the context environment.

Why does Life needs threads?

Independent of our user's other activities, we would like our applet to run continuously, looping generation through generation until told to stop. We want to computer to give continuous resources to the running of our applet. The typical applet does not have this continuous resource. When an event occurs, then certain methods are run, such as the paint method. But this thread is not ours to capture. The operating system gives our code temporary use of a drawing thread and we should give control back as soon as possible, after drawing our applet.

So we create a new thread and attach our run method to it. Java abstracts a thread as a Thread object whose constructor requires a object of type Runnable. Runnable is an Interface. Therefore, a class is of type Runnable if it implements the Runnable Interface.

After a Thread object is constructed, the thread's start method means that our run method is called, within a the newly created thread. It can run forever. Typically run methods contain infinite loops.

So what's so hard about that?

Threads each have a context, but some of that context is shared. If two threads are each given a reference to the same object on construction, and their start methods are called, then within the single object environment of the single object, two threads will be running. That means that variable values might change even though one thread did not make an assignment to that variable. The other thread might have made the assignment. This is very tricky.

In our case, button events are refered to the ActionListener, which is the same object as the Runnable object. The button event is run in a second thread, but in the same object environment, as the Runnable thread. Variables that the run method references might be changed simultaneously by the actionPerformed method, if the button is pushed at the same time as the runnable thread is doing work.

So what must I do?

To get this situation absolutely correct, the synchronized keywork or synchronized methods must be used to prevent multiple threads from confusing each other. We will, however, ignore all of this. We might produce code with bad timing-bugs ... bugs that show up only when a sequence of requests are made within particular time windows ... but good enough for now.

We will provide you with the following pattern of thread use, which will be sufficient for the Life game.


A simple pattern for thread use.

The ThreadExample applet has two buttons which control the running of a simple thread which counts on System.out. To see the output open the java console of your browser: