Multithreading allows two parts of the same program to run concurrently. This article discusses how to pull off this performance-improving feat in Java. It is excerpted from chapter 10 of the book Java Demystified, written by Jim Keogh (McGraw-Hill/Osborne, 2004; ISBN: 0072254548).
The operating system must do extra work to manage multitasking. Programmers call this extra work overhead because resources inside your computer are used to manage the multitasking operation rather than being used by programs for processing instructions and data.
Process-based multitasking has a larger overhead than thread-based multitasking. In process-based multitasking, each process requires its own address space in memory. The operating system requires a significant amount of CPU time to switch from one process to another process. Programmers call this context switching, where each process (program) is a context. Additional resources are needed for each process to communicate with each other.
In comparison, the threads in thread-based multitasking share the same address space in memory because they share the same program. This also has an impact on context switching, because switching from one part of the program to another happens within the same address space in memory. Likewise, communication among parts of the program happens within the same memory location.
A thread is part of a program that is running. Thread-based multitasking has multiple threads running at the same time (that is, multiple parts of a program running concurrently). Each thread is a different path of execution.
Letís return to the word processing program example to see how threads are used. Two parts of the word processor are of interest: The first is the part of the program that receives characters from the keyboard, saves them in memory, and displays them on the screen. The second part is the portion of the program that checks spelling. Each part is a thread that executes independently of each other, even though they are part of the same program. While one thread receives and processes characters entered into the keyboard, the other thread sleeps. That is, the other thread pauses until the CPU is idle. The CPU is normally idle between keystrokes. It is this time period when the spell checker thread awakens and continues to check the spelling of the document. The spell checker thread once again pauses when the next character is entered into the keyboard.
The Java run-time environment manages threads, unlike in process-based multitasking where the operating system manages switching between programs. Threads are processed asynchronously. This means that one thread can pause while other threads continue to process.
A thread can be in one of four states:
Running A thread is being executed.
Suspended Execution is paused and can be resumed where it left off.
Blocked A resource cannot be accessed because it is being used by another thread.
Terminated Execution is stopped and cannot be resumed.
All threads are not equal. Some threads are more important than other threads and are giving higher priority to resources such as the CPU. Each thread is assigned a thread priority that is used to determine when to switch from one executing thread to another. This is called context switching.
A threadís priority is relative to the priority of other threads. That is, a threadís priority is irrelevant if it is the only thread that is running. A lower-priority thread runs just as fast as a higher-priority thread if no other threads are executing concurrently.
Thread priorities are used when the rules of context switching are being applied. These rules are as follows:
A thread can voluntarily yield to another thread. In doing so, control is turned over to the highest-priority thread.
A higher-priority thread can preempt a lower-priority thread for use of the CPU. The lower-priority thread is paused regardless of what itís doing to give way to the higher-priority thread. Programmers call this preemptive multitasking.
Threads of equal priority are processed based on the rules of the operating system that is being used to run the program. For example, Windows uses time slicing, which involves giving each high-priority thread a few milliseconds of CPU cycles, and keeps rotating among the high-priority threads. In Solaris, the first high-priority thread must voluntarily yield to another high-priority thread. If it doesnít, the second high-priority thread must wait for the first thread to terminate.
Multithreading occurs asynchronously, meaning one thread executes independently of the other threads. In this way, threads donít depend on each otherís execution. In contrast, processes that run synchronously depend on each other. That is, one process waits until the other process terminates before it can execute.
Sometimes the execution of a thread is dependent on the execution of another thread. Letís say you have two threadsóone handles gathering login information, and the other validates a userís ID and password. The login thread must wait for the validation thread to complete processing before it can tell the user whether or not the login is successful. Therefore, both threads must execute synchronously, not asynchronously.
Java enables you to synchronize threads by defining a synchronized method. A thread that is inside a synchronized method prevents any other thread from calling another synchronized method on the same object. Youíll learn how to do this later in the chapter.