This leaves us only one more term to define: what exactly is a thread? The term thread is shorthand for thread of control, and a thread of control is, at its simplest, a section of code executed independently of other threads of control within a single program.
We’re all familiar with the use of multitasking operating systems to run multiple programs simultaneously. Each of these programs has at least one thread within it, so at some level, we’re already comfortable with the notion of a thread in a single process. The single-threaded process has the following properties, which, as it turns out, are shared by all threads in a program with multiple threads as well:
The process begins execution at a well-known point. In programming
languages like C and C++ (not to mention Java itself), the thread
begins execution at the first statement of the function or method
Execution of the statements follows in a completely ordered, predefined sequence for a given set of inputs. An individual process is single-minded in this regard: it simply executes the next statement in the program.
While executing, the process has access to certain data. In Java, there are three types of data a process can access: local variables are accessed from the thread’s stack, instance variables are accessed through object references, and static variables are accessed through class or object references.
Now consider what happens when you sit at your computer and start two single-threaded programs: a text editor, say, and a file manager. You now have two processes running on your computer; each process has a single thread with the properties just outlined. Each process does not necessarily know about the other process, although, depending on the operating system running on your computer, there are several ways in which the processes can send each other various messages. A common behavior is that you can drag a file icon from the file manager into the text editor in order to edit the file. Each process thus runs independently of the other, although they can cooperate if they so choose. The typical multitasking environment is shown in Figure 1.1.
From the point of view of the person using the computer, these processes often appear to execute simultaneously, although many variables can affect that appearance. These variables depend on the operating system: for example, a given operating system may not support multitasking at all, so that no two programs appear to execute simultaneously. Or the user may have decided that a particular process is more important than other processes and hence should always run, shutting out the other processes from running and again affecting the appearance of simultaneity.
Finally, the data contained within these two processes is, by default, separated: each has its own stack for local variables, and each has its own data area for objects and other data elements. Under many operating systems, the programmer can make arrangements so that the data objects reside in memory that can be shared between the processes, allowing both processes to access them.
All of this leads us to a common analogy: we can think of a thread just as we think of a process, and we can consider a program with multiple threads running within a single instance of the Java virtual machine just as we consider multiple processes within an operating system, as we show in Figure 1.2.
So it is that within a Java program, multiple threads have these properties:
Each thread begins execution at a predefined, well-known location.
For one of the threads in the program, that location is the
main() method; for the rest of the threads, it
is a particular location the programmer decides on when the code is
written. Note that this is true of an applet as well, in which case
main() method was executed by the browser
Each thread executes code from its starting location in an ordered, predefined (for a given set of inputs) sequence. Threads are single-minded in their purpose, always simply executing the next statement in the sequence.
Each thread executes its code independently of the other threads in the program. If the threads choose to cooperate with each other, there are a variety of mechanisms we will explore that allow that cooperation. Exploiting those methods of cooperation is the reason why programming with threads is such a useful technique, but that cooperation is completely optional, much as the user is never required to drag a file from the file manager into the text editor.
The threads appear to have a certain degree of simultaneous execution. As we’ll explore in Chapter 6, the degree of simultaneity depends on several factors—programming decisions about the relative importance of various threads as well as operating system support for various features. The potential for simultaneous execution is the key thing you must keep in mind when threading your code.
The threads have access to various types of data. At this point, the analogy to multiple processes breaks down somewhat, depending on the type of data the Java program is attempting to access.
Each thread is separate, so that local variables in the methods that the thread is executing are separate for different threads. These local variables are completely private; there is no way for one thread to access the local variables of another thread. If two threads happen to execute the same method, each thread gets a separate copy of the local variables of that method. This is completely analogous to running two copies of the text editor: each process would have separate copies of the local variables.
Objects and their instance variables, on the other hand, can be shared between threads in a Java program, and sharing these objects between threads of a Java program is much easier than sharing data objects between processes in most operating systems. In fact, the ability to share data objects easily between threads is another reason why programming with threads is so useful. But Java threads cannot arbitrarily access each other’s data objects: they need permission to access the objects, and one thread needs to pass the object reference to the other thread.
Don’t panic over this analogy: the fact that you’ll be programming with threads in Java doesn’t mean you’ll necessarily be doing the system-level type of programming you’d need to perform if you were writing the multitasking operating system responsible for running multiple programs. The Java Thread API is designed to be simple and requires little specialized skill for most common tasks.