Thread Definition

A thread is a single flow of control within a program. It is sometimes called the execution context because each thread must have its own resources, like the program counter and the execution stack, as the context for execution. However, all threads in a program still share many resources, such as memory space and opened files. Therefore, a thread may also be called a lightweight process.

NOTE: It is easier to create and destroy a thread than a process.

Parallel and Concurrent Threads

When two threads run in parallel, they are both being executed at the same time on different CPUs. Two concurrent threads are both in progress, or trying to get some CPU time for execution at the same time, but are not necessarily being executed simultaneously on different CPUs.

Improved CPU Utilization
A program may spend a big portion of its execution time waiting. For example, it may wait for a resource to become accessible in an I/O operation, or it may wait for a time-out to occur to start drawing the next scene of an animation sequence. To improve CPU utilization, tasks with potentially long waits can run as separate threads. Once a task starts waiting for something to happen, the Java run time can choose another runnable task for execution

Single-Threaded Program Example
In the program shown here, a run() method in the NoThreadPseudoIO class is created to simulate a 10-second I/O operation. The main program will first perform the simulated I/O operation

Single-Threaded Program Methods
The method showElapsedTime() is defined to print the time elapsed in seconds since the program started, together with a user-supplied message. The currentTimeMillis() method of the System class in the java.lang package will return a long integer for the time difference, measured in milliseconds, between the current time and 00:00:00 GMT on January 1, 1970.

NOTE: The times shown will vary depending on machine types and environments.

Single-Threaded Program Methods
The method showElapsedTime() is defined to print the time elapsed in seconds since the program started, together with a user-supplied message. The currentTimeMillis() method of the System class in the java.lang package will return a long integer for the time difference, measured in milliseconds, between the current time and 00:00:00 GMT on January 1, 1970.

NOTE: The times shown will vary depending on machine types and environments.

Multithreaded Program Example
The multithreaded program declares the class for the simulated I/O operation as a subclass of the Thread class. After the thread is created, the multithreaded program uses the start() method of the Thread class to start the I/O operation. The start() method in turn calls the run() method of the subclass.

Creating and Running Threads
There are two methods for making a task run concurrently with other tasks: create a new class as a subclass of the Thread class or declare a class by implementing the Runnable interface

Use Subclass
When you create a subclass of the Thread class, this subclass should define its own run() method to override the run() method of the Thread class. This run() method is where the task is performed.

Execute run() Method
The run() method is the first user-defined method the Java runtime calls when a thread is started. An instance of the subclass is then created by a new statement, followed by a call to the thread's start() method to have the run() method executed.

Implement Runnable Interface
The Runnable interface requires only one method to be implemented-the run() method. You first create an instance of this class with a new statement, followed by the creation of a Thread instance with another new statement. Finally, call this thread instance's start() method to start performing the task defined in the run() method

Indicate run() Method
A class instance with the run() method defined within it must be passed in as an argument in creating the Thread instance, so that when the start() method of this Thread instance is called, Java run time knows which run() method to execute.

Re-Implement Multithreaded Program
A multithreaded program can be re-implemented using the Runnable interface by first changing the class definition to implement the Runnable interface, instead of subclassing the Thread class. An instance of the class is created and passed to a newly created Thread instance, followed by a call to the start() method to start the execution of the run() method.

Controlling Thread Executions
Many methods defined in the java.lang.Thread class control the running of a thread. Java 2 deprecated several of them to prevent data inconsistencies or deadlocks. If you are just starting with Java 2, avoid the deprecated methods and use the equivalent behavior. However, if you are transitioning from Java 1.0 or 1.1, you will need to modify your code to avoid the deprecated methods if you used them. Some of the most commonly used methods are examined in this section.

void start
The void start() method is used to start the execution of the thread body defined in the run() method. Program control will be immediately returned to the caller, and a new thread will be scheduled to execute the run() method concurrently with the caller's thread.

void stop
The void stop() method is deprecated and used to stop the execution of the thread no matter what the thread is doing. The thread is then considered dead, the internal states of the thread are cleared, and the resources allocated are reclaimed. Using this method has the potential to leave data in an inconsistent state and should be avoided.

void suspend
The void suspend() method is deprecated and used to temporarily stop the execution of the thread. All the states and resources of the thread are retained. The thread can later be restarted by another thread calling the resume() method. Using this method has a strong potential for deadlocks and should be avoided. You should use the Object.wait() method instead.

void resume
The void resume() method is deprecated and used to resume the execution of a suspended thread. The suspended thread will be scheduled to run. If it has a higher priority than the running thread, the running thread will be preempted; otherwise, the just-resumed thread will wait in the queue for its turn to run. Using this method has a strong potential for deadlocks and should be avoided. You should use the Object.notify() method instead.

static void sleep
The static void sleep() method is a class method that causes the Java run time to put the caller thread to sleep for a minimum of the specified time period. The exception, InterruptedException, may be thrown while a thread is sleeping or any time if you interrupt() it. Either a try-catch statement needs to be defined to handle this exception or the enclosing method needs to have this exception in the throws clause.

void join
The void join() method is used for the caller's thread to wait for this thread to die-for example, by coming to the end of the run() method.

static void yield
The static void yield() method is a class method that temporarily stops the caller's thread and puts it at the end of the queue to wait for another turn to be executed. It is used to make sure other threads of the same priority have the chance to run.

Caller's Thread
All the class methods defined in the Thread class, such as sleep() and yield(), will act on the caller's thread. That is, it is the caller's thread that will sleep for a while or yield to others.

NOTE: The reason is that a class method can never access an instance's data or method members unless the instance is passed in as an argument, created inside the method, or stored in a class variable visible to the method.

Thread Execution Example
In the example shown here, the main thread creates two threads, and then waits for the first thread to finish by calling the first thread's join() method. The first thread calls the sleep() method to be asleep for 10 seconds.

Second Thread
The second thread calls its own wait() method to suspend itself. After the first thread comes to an end, the main thread will resume its execution, wake up the second thread by calling the second thread's notify() method, and wait until the second thread also comes to an end by calling the second thread's join() method.

Thread Life Cycle
Every thread, after creation and before destruction, will always be in one of four states: newly created, runnable, blocked, or dead

New Threads
A thread enters the newly created state immediately after creation; that is, it enters the state right after the thread-creating new statement is executed. In this state, the local data members are allocated and initialized, but execution of the run() method will not begin until its start() method is called. After the start() method is called, the thread will be put into the runnable state.

Runnable Threads
When a thread is in the runnable state, the execution context exists and the thread can be scheduled to run at any time; that is, the thread is not waiting for any event to happen.

Running and Queued States
The runnable state can be subdivided into two substates: the running and queued states. When a thread is in the running state, it is assigned CPU cycles and is actually running. When a thread is in the queued state, it is waiting in the queue and competing for its turn to spend CPU cycles. The transition between these two substates is controlled by the virtual machine scheduler.

NOTE: A thread can call the yield() method to voluntarily move itself to the queued state from the running state.

Blocked Threads
The blocked state is entered when one of these events occurs: the thread or another thread calls the suspend() method; the thread calls an object's wait() method; the thread calls the sleep() method; the thread is waiting for an I/O operation to complete; or the thread will join() with another thread.

CPU Cycle Competition
A thread in a blocked state will not be scheduled for running. It will go back to the runnable state, competing for CPU cycles, when the counter-event for the blocking event occurs.

Dead Threads
The dead state is entered when a thread finishes its execution or is stopped by another thread calling its stop() method.

NOTE: The stop method is deprecated.

Boolean Conditions
To avoid the use of stop(), the proper way to exit out of a while (true) loop is to maintain a state variable that is used as the while loop condition check. So, instead of using stop() to halt the thread, you would change the test case to be a boolean condition. Then, when you want the thread to stop, instead of calling stop(), you change the state of the boolean. This causes the thread to stop on the next pass and ensures that the thread does not leave data in an inconsistent state.
//----------------------
boolean done = false;
public void run() {
while(!done) {
....
}
}

public safeStop() {
done = true;
}
//-------------------------

Thread Status
To find out whether a thread is alive-that is, currently runnable or blocked-use the thread's isAlive() method. It will return true if the thread is alive.

NOTE: If a thread is alive, it does not mean it is running, just that it can run.

Thread Synchronization

Introduction
All threads in a program share the same memory space, making it possible for two threads to access the same variable or run the same method of the same object at the same time.

Potential Problems
Problems may occur when multiple threads are accessing the same data concurrently. Threads may race each other, and one thread may overwrite the data just written by another thread. Or, one thread may work on another thread's intermediate result and break the consistency of the data. A mechanism is needed to block one thread's access to the critical data, if the data is being worked on by another thread.

Bank Account Example
Suppose you have a program to handle a user's bank account. There are three subtasks in making a deposit for the user: get the current balance from a remote server, which may take as long as five seconds; add the newly deposited amount into the just-acquired balance; and send the new balance back to the same remote server, which, again, may take as long as five seconds to complete.

Deposit Scenario
If two depositing threads, each making a $1,000 deposit, are started at roughly the same time on a current balance of $1,000, the final balance of these two deposits may reflect the result of only one deposit. In the possible scenario shown here, the balance stored in the remote server increases by only one deposit amount.

Deposit Scenario Program
The program shown here simulates the deposit scenario. An Account class is defined with three methods: getBalance() to fetch the current balance from some pseudo-server, with a simulated five-second delay; setBalance() to write back the new balance to the same pseudo-server, with (again) a simulated five-second delay; and deposit() to use the other two methods to complete a deposit transaction.

Start Deposit Operation
A DepositThread class is declared to start the deposit operation on the account. The main program creates an account instance and then starts two threads to make a deposit of $1,000 each to that account.

Monitor Model
Java uses the idea of monitors to synchronize access to data. A monitor is a guarded place where all the protected resources have the same locks. Only a single key fits all the locks inside a monitor, and a thread must get the key to enter the monitor and access these protected resources.

Monitor Key
If many threads want to enter the monitor simultaneously, only one thread is handed the key; the others must wait outside until the key-holding thread finishes its use of the resources and hands the key back to the Java virtual machine.

NOTE: Deadlock may occur if threads are waiting for each other's key to proceed.

Thread Access
Once a thread gets a monitor's key, the thread can access any of the resources controlled by that monitor countless times, as long as the thread still owns the key. However, if this key-holding thread wants to access the resources controlled by another monitor, the thread must get that particular monitor's key.

Multiple Monitor Keys
At any time, a thread can hold many monitors' keys. Different threads can hold keys for different monitors at the same time.

Protected Resources
In Java, the resources protected by monitors are program fragments in the form of methods or blocks of statements enclosed in curly braces. If some data can be accessed only through methods or blocks protected by the same monitor, access to the data is indirectly synchronized.

synchronized Keyword
You use the keyword synchronized to indicate that the following method or block of statements is to be synchronized by a monitor. When a block of statements is to be synchronized, an object instance enclosed in parentheses immediately following the synchronized keyword is required so the Java virtual machine knows which monitor to check. For example, the deposit() method can be synchronized to allow only one thread to run at a time. The only change needed is a synchronized keyword before the method definition.

Called Object Synchronization
Alternatively, a block of statements in the deposit() method can be synchronized on the called object.

Alternative Program
The output of the alternative program is almost the same, except the first message from the second thread will be interleaved in the messages from the first thread, because the first println() method is not inside the synchronized block.

Unique Key
One unique key will be issued to every object containing a synchronized instance method or being referred by a synchronized block. For synchronized class methods, the key is issued to the class because the method may be called before any class instances exist. This means that every object and every class can have a monitor if there are any synchronized methods or blocks of statements associated with them. Furthermore, a class monitor's key is different from any of the keys of its class instance monitors

Synchronization Techniques
There are differences between a synchronized method and a synchronized block, and differences between class-based synchronization and object-based synchronization.

Synchronization Schemes
In the example shown here, Class SyncToken contains three methods, all synchronized differently and all calling the ticker() method to print out three ticks in random intervals. Class SyncTestRunner is a thread class that will choose different methods of class SyncToken to run based on the ID given. The main() method of the SyncTest class will generate 10 threads running the tickers with different synchronization schemes.

Comparisons
Object-based synchronized methods and synchronized blocks share the same monitor key if they are for the same object. Also, class-based synchronization and object-based synchronization use different keys because their output interleaves each other.

Negative and Positive Aspects
Synchronization is an expensive operation, and the use of it should be kept to a minimum, especially for frequently executed methods or blocks of statements. However, synchronization can help reduce the interference among different threads and improve the stability and robustness of a program.

In summary, multithreading allows multiple tasks to execute concurrently within a single program. There are two methods for making a task run concurrently with other tasks: create a new class as a subclass of the Thread class or declare a class implementing the Runnable interface. Many methods defined in the java.lang.Thread class control the running of a thread. Java 2 deprecated several of them to prevent data inconsistencies or deadlocks.

Every thread will always be in one of four states: newly created, runnable, blocked, or dead. Every thread instance is a member of one thread group. All threads and thread groups in an application form a tree, with the system thread group as the root.

Synchronization is the way to avoid data corruption caused by simultaneous access to the same data. Java uses monitors to synchronize access to data. A monitor is a guarded place where all the protected resources have the same locks.
 

Advanced Multithreading

 Overview
Using thread-control methods, threads communicate by waiting for each other. This module will teach you about advanced multithreading issues including inter-thread communication, thread priority values, and preemptive thread scheduling. You will learn about thread local variables and daemon threads

Objectives
The objectives covered in this module are:
Use thread-control methods to allow threads to communicate by waiting for each other
Use thread priority values to ensure important or time-critical threads are executed frequently or immediately and use scheduling to ensure priorities are enforced
Use thread local variables to permit thread instances to have independent copies of variables and use daemon threads to provide services to other threads
 
Inter-Thread Communications
Inter-thread communications enable threads to talk to or wait for each other. Upon completion of this lesson, you will be able to:
Identify two ways threads communicate with each other
Describe thread-control methods
Use thread-control methods to solve problems between consumers and producers
 
Inter-Thread Communications
Inter-thread communications enable threads to talk to or wait for each other. Upon completion of this lesson, you will be able to:
Identify two ways threads communicate with each other
Describe thread-control methods
Use thread-control methods to solve problems between consumers and producers
 

Introduction
Threads can communicate with each other through shared data. You can also use thread-control methods to have threads wait for each other.

Shared Data
All the threads in the same program share the same memory space. If the reference to an object is visible to different threads by the syntactic rules of scopes, or explicitly passed to different threads, these threads share access to the data members of that object.
 
 Synchronization
Synchronization is sometimes necessary to enforce exclusive access to the data to avoid racing conditions and data corruption.
 
Thread-Control Methods
Using thread-control methods, threads communicate by waiting for each other. For example, the join() method can be used for the caller thread to wait for the completion of the called thread. Also, a thread can suspend itself and wait at a rendezvous point using the suspend() method; another thread can wake it up through the waiting thread's resume() method, and both threads can run concurrently thereafter.
 
Deadlock
Deadlock may occur when a thread holding the key to a monitor is suspended or waiting for another thread's completion. If the other thread it is waiting for needs to get into the same monitor, both threads will be waiting forever. This is why the suspend() and resume() methods are now deprecated and should not be used. The wait(), notify(), and notifyAll() methods defined in class Object of the java.lang package can be used to solve this problem.

wait() Method
The wait() method will make the calling thread wait until either a time-out occurs or another thread calls the same object's notify() or notifyAll() method.

NOTE: The former will wait until the thread is notified. The latter will wait until either the specified time-out expires or the thread is notified, whichever comes first.

Process
When a thread calls the wait() method, the key it is holding will be released for another waiting thread to enter the monitor. The notify() method will wake up only one waiting thread, if any. The notifyAll() method will wake up all the threads that have been waiting in the monitor. After being notified, the thread will try to reenter the monitor by requesting the key again and may need to wait for another thread to release the key.
 
Monitor
The wait(), notify(), and notifyAll() methods can be called only within a monitor or synchronized block. The thread calling an object's notify() or notifyAll() method needs to own the key to that object's monitor; otherwise, IllegalMonitorStateException, a type of RuntimeException, will be thrown.
 

Real-World Application
You can use the wait() and notify() methods to solve a problem between a producer and a consumer. In this problem, the producer will generate data for the consumer to consume. However, if the producer produces data faster than the consumer can consume, the newly created data may be overwritten before it is consumed. Also, if the consumer consumes faster than the producer can produce, the consumer may keep using already processed data.
 

First Implementation
The first implementation uses a monitor, an instance of the NoWaitMonitor class, to control the access to the data, token. The producer and consumer will set and get, respectively, the token value in random intervals, with the maximum interval length regulated by the speed argument passed to their constructors.
 
Main Program (Ver programa NoWaitPandC.java)
The main program accepts up to two command-line arguments for setting the producing and consuming speed, creates an instance of the monitor, creates a producer and a consumer, and watches them run for 10 seconds.
 
Producer Outpaces Consumer
In this program output, the producer outpaces the consumer. There is a lot of data generated (shown as Set) but overwritten before it is processed (shown as Got).
 
Consumer Outpaces Producer
In this program output, the consumer is faster than the producer. This time, some of the data is processed multiple times.
 
Second Implementation
The second implementation of the program uses the wait() and notify() methods to make sure all data is created and used exactly once. The program is the same, except for the implementation of the monitor. A boolean variable, valueSet, is added to indicate whether the data is ready for consumption or already used.

get() Method
The get() method will first test if the data is ready for consumption. If not, the calling thread will wait until some other thread sets the data and notifies the current thread. The boolean variable is then set to indicate that the data is consumed. Any thread waiting to produce new data will then be notified to start production. If there is no thread waiting to produce, the notify() method will be ignored.
 
set() Method
Symmetrically, the set() method will first test whether the data is already used. If not, the calling thread will wait until some other thread uses the data and notifies the current thread. Next, the boolean variable is set to indicate that the data is ready for consumption. Any thread waiting to consume the data will then be notified to start the consumption. If there is no thread waiting, the notify() method will be ignored.

Data Consumption (New version of the program RealWorldApplication.java)
The full program listing is shown here. This time, every piece of data generated is consumed exactly once.

NOTE: To download a copy of the source code discussed in this section, click here.