13 Multithreading — Java Programming
13.1 Multithreading Basics
In concurrent programming, there are two basic units of execution: processes and threads. The ability to have more than one program working at the same time is called multitasking. The main objective of multi tasking is to minimise execution time for faster delivery of result and maximize CPU Utilisation by running several tasks at the same time. It can be done in two ways, depending on whether the operating system interrupts programs without consulting with them first, or whether programs are only interrupted when they are willing to yield control. The former is called pre-emptive multitasking. The latter is called cooperative multitasking or non-preemptive multitasking.
Multi tasking can be done using two methods: i) multi processing, ii) multithreading
13.1.1 Multiprocessing
Multi tasking using multi processing requires multiple processors. A simple java program normally requires one processor. If a user simultaneously wants to run another process, the necessary actions are taken up to submit it to another processor in the cores.
13.1.2 Multithreading
Multithreading programs extend the idea of multitasking by taking it one level lower. Individual programs will appear to do multiple tasks at the same time. Each task is called a thread and programs that can run more than one thread at once are said to be multithreaded. It is very useful in practice. For example, the Java virtual machine itself uses a thread for garbage collection. The GUI programs have a separate thread for gathering user interface events from the host-operating environment.
13.1.3 Multiprocessing vs Multithreading
Multitasking threads require minimum overheads than multitasking processes. Processes are heavy tasks that require their own separate address spaces. Inter process communication is expensive and limited. Context switching from one process to other is also costly. Threads, on the other hand, are light components. They share the same address space and cooperatively share the same heavy process. Inter thread communication is inexpensive, and context switching from one thread to the next is of low cost.
13.2 The Java Thread Model
In an operating system context, a thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system. In many cases, a thread is a component of a process. [13.1]
A thread is thus a smallest portion of the process. A thread is an independent, virtual and sequential control flow within a process. Each thread performs the job independently of another thread. The multiple threads of a given process may be executed concurrently (via multithreading capabilities), sharing resources such as memory, while different processes do not share these resources. In particular, the threads of a process share its executable code and the values of its dynamically allocated variables and non-thread-local global variables at any given time.
The Java run-time system depends on threads for many things, and all the class libraries are designed with multithreading in mind. In fact, Java uses threads to enable the entire environment to be asynchronous. This helps reduce inefficiency by preventing the waste of CPU cycles. In fact, Java uses threads to enable the entire environment to be asynchronous. This helps reduce inefficiency by preventing the waste of CPU cycles. In multithread environment one thread can pause without stopping other parts of the program.
Thread exists in several states. A thread can be running. It can be ready to run as soon as it gets CPU time. A running thread can be suspended, which temporarily suspends its activity. A suspended thread can then be resumed, allowing it to pickup where it left off. A thread can be blocked when waiting for a resource. At any time, a thread can be terminated, which halts its execution immediately. Once terminated, a thread cannot be resumed. The different state of a thread and the transitions from one state to other can be represented as follows.
13.3 Context Switching and Threads
Java assigns to each thread a priority that determines how that thread should be treated with respect to others. Thread priorities are integers that specify the relative priority of one thread to another. A higher priority thread doesn’t run any faster than a lower priority thread if it is the only thread running. Instead, a thread’s priority is used to decide when to switch from one running thread to the next. This is called context switch. The rules that determine when a context switch takes place are simple.
A thread can voluntarily relinquish control. This is done by explicitly yielding, sleeping or blocking on pending I/O. In this scenario, all other threads are examined, and the highest-priority thread that is ready to run is given the CPU time.
A higher priority thread can pre-empt a lower-priority thread. In this case, a lower priority thread that does not yield the processor is simply pre-empted, no matter what it is doing. This is called preemptive multitasking.
13.4 Synchronization
Because multithreading introduces an asynchronous behaviour to the programs, there must be a way to enforce synchronization. For Example, if you want two threads to communicate and share a complicated data structure, such as a linked list, you need to ensure that they don’t conflict with each other. You must prevent one thread from writing data while another thread is in the middle of reading. Java uses a monitor to enforce synchronization. Once a thread enters a monitor, all other threads must wait until that thread exits the monitor. Each object has its own implicit monitor that is automatically entered when one of the object’s synchronized methods is called. Once a thread is inside a synchronous method, no other thread can call any other synchronized method on the same object.
There should be inter-thread communications to accomplish synchronization. When programming other languages, you must depend on the operating system to establish communication between threads. Java provides a clean, low-cost way for two-or more threads to talk to each other, via calls to predefined methods that all objects have. Java’s messaging system allows a thread to enter a synchronized method on an object, and then wait there until some other thread explicitly notifies it to come out.
Java’s multithreading system is built upon the Thread class and the Runnable interface. Thread encapsulates a thread of exception. Since one cannot directly refer to the real state of a running thread, you should deal with it through an instance of Thread that spawned it. To create a new thread the program will either extend Thread class or implements the Runnable interface. The methods that are provided by the Thread class and are most frequently used by the programs can be summarized as in the following table.
Type | Field |
static int |
MAX_PRIORITY // The maximum priority that a thread can have. |
static int |
MIN_PRIORITY // The minimum priority that a thread can have. |
static int |
NORM_PRIORITY // The default priority that is assigned to a thread. |
Constructors | |
Thread() |
Allocates a new Thread object. |
Thread(Runnable target) |
Allocates a new Thread object. |
Type | Methods in Thread class | Description |
static int |
activeCount() |
Returns the current number of active threads in the VM. |
static Thread |
currentThread() |
Returns a reference to the currently executing thread object. |
int |
getPriority() |
Returns this thread’s priority. |
boolean |
isAlive() |
Tests if this thread is alive. |
void |
join() |
Waits for this thread to die. |
void |
run() |
If this thread was constructed using a separate Runnable run object, then that Runnable object’s run method is called; otherwise, this method does nothing and returns. |
void |
setPriority(int newPriority) |
Changes the priority of this thread. |
static void |
sleep(long millis) |
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds. |
void |
start() |
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. |
String |
toString() |
Returns a string representation of this thread, including a unique number that identifies the thread and the thread’s priority. |
static void |
yield() |
Causes the currently executing thread object to temporarily pause and allow other threads to execute. |
When a Java program starts up, one thread begins running immediately. This is usually called the main thread of the program, because it is the one executed when your program begins. The main thread is important because:
- It is the thread from which other “child” threads will be spawned.
- It must be the last thread to finish execution. When the main thread stops, your program terminates.
You can create a thread by instantiating an object of type Thread. Java defines two ways in which this can be accomplished:
- Implementing the Runnable interface
- Extending the Thread class.
The main thread can be controlled through a Thread object. To do so, you must obtain a reference to it by calling the method currentThread(), which is public and static member of Thread class.
Program 13.1 The Following program tries to suspend the program in each iteration of the loop through the currentThread.
//mainThread.java import java.lang.*; public class mainThread extends Thread { public static void main(String args[]) { Thread t= Thread.currentThread(); System.out.println("Current thread is:" +t); t.setName("My thread"); System.out.println("Current thread after renaming is:" +t); for(int n=5;n>0;n--) { System.out.println(n); try { Thread.sleep(1000); } catch(InterruptedException ex) { System.out.println("Main thread Interrpted"); } } } } /* Output: C:\java\bin\javabook>java mainThread Current thread is:Thread[#1,main,5,main] Current thread after renaming is:Thread[#1,My thread,5,main] 5 4 3 2 1 */
13.5 Runnable Interface
The easiest way to create a thread is to create a class that implements Runnable interface. A thread can be constructed on any object that implements the Runnable interface. To implement it only the run() method is to be implemented which is declared a public void. Inside the run(), you define the code that constitutes the new thread. The run() establishes the entry point from another concurrent thread of execution within the program. The thread ends when the run() methods ends. After a thread is created, it will not start running until you call its start() method, which is declared within Thread. In essence start() executes a call to run() method.
Program 13.2 Program to create new thread using Runnable interface
//NewThread.java
class NewThread implements Runnable { NewThread() { } public static void main(String args[]) { NewThread n=new NewThread(); Thread t =new Thread(n); System.out.println("created new thread"); t.start(); System.out.println("Started new thread"); } public void run() { System.out.println("This is a thread"); } } /* Output: created new thread Started new thread This is a thread */
There is a probability that the order of the last two statements may be reversed because the main thread may be suspended immediately after the start() method is called and the thread first prints its message. Then the main method may be resumed and then print its message. But most probably the output will be as above. This is because there lies overhead of creating and starting a new thread. The main thread can print its message before all overheads are completed.
The second way to create a thread is to create a new class that extends Thread, and then create an instance of that class. The extending class must override the run() method which is the entry point for the thread. It must call the start() method to start the execution of the new thread.
Program 13.3 Program to override the run() method using extends Thread
//NewThread1.java class NewThread1 extends Thread { NewThread1() { } public static void main(String rgs[]) { NewThread1 n = new NewThread1(); Thread t = new Thread(n); System.out.println("created new thread"); t.start(); System.out.println("Started new thread"); } public void run() { System.out.println("This is a thread"); } } /* Output: created new thread Started new thread This is a thread */
The main thread must be the last thread to finish execution. One way to ensure this is to call the sleep() method with enough delay before which all the child threads will finish execution. This will surely be a problem if we cannot estimate the delay correctly. Fortunately the Thread class provides two methods to overcome this problem. They are:
final Boolean isAlive() final void join()
The isALive() method returns true if the thread upon which it is called is still running. It returns false otherwise. The joint() method waits until the thread on which it is called terminated. Its name comes from the concepts of the calling thread waiting until the specified thread joins it.
Program 13.4. Program to demonstrate join() and create multiple threads
//NewThreads.java class NewThreads implements Runnable { String str; Thread t; NewThreads(String name) { str=name; t=new Thread(this,name); System.out.println("New thread created :"+t); t.start(); } public static void main(String args[]) { NewThreads n1 = new NewThreads("first"); NewThreads n2 = new NewThreads("second"); NewThreads n3 = new NewThreads("Third"); try { n1.t.join(); n2.t.join(); n3.t.join(); } catch(InterruptedException excep) { System.out.println(" Main thread interrupted "); } System.out.println("Thread one is alive :"+n1.t.isAlive()); System.out.println("Thread two is alive :"+n2.t.isAlive()); System.out.println("Thread three is alive :"+n3.t.isAlive()); } public void run() { for(int i=3; i>=1;i--) { System.out.println(str +":"+i); } } } /* Output: C:\java\bin\javabook>java NewThreads New thread created :Thread[#29,first,5,main] New thread created :Thread[#30,second,5,main] New thread created :Thread[#31,Third,5,main] Third:3 Third:2 second:3 second:2 second:1 first:3 Third:1 first:2 first:1 Thread one is alive :false Thread two is alive :false Thread three is alive :false */
13.6 Thread priorities
The thread scheduler, to decide when each thread should be allowed to run, uses thread priorities. In practice, the amount of CPU time that a thread gets often depends on several factors besides its priority. A higher-priority thread also can pre-empt a lower-priority thread. To set a thread’s priority, the setPriority (int level) method should be called. Here the level specifies the new priority setting for the calling thread. The value of level must be 1 and 10 respectively. The default priority is 5. To know the priority of a thread we can use the getPriority() method.
Program 13.5. Program to demonstrate the thread priority
//PriorityThread.java import java.lang.*; class PriorityThread implements Runnable { private volatile boolean running=true; Thread t; int clicks=0; PriorityThread(int level) { t=new Thread(this); t.setPriority(level); t.start(); } public void run() { while(running) clicks++; } public void stop() { running=false; } public static void main(String args[]) { PriorityThread p1=new PriorityThread(2); PriorityThread p2=new PriorityThread(8); try { Thread.sleep(2000); } catch(InterruptedException iexcp) { System.out.println("Main thread interrupted"); } p1.stop(); p2.stop(); System.out.println("Thread with priority 2 clicked:"+p1.clicks); System.out.println("Thread with priority 8 clicked:"+p2.clicks); } } /* Output: C:\java\bin\javabook>java PriorityThread Thread with priority 2 clicked:1229991824 Thread with priority 8 clicked:1226609637 */
13.7 Summary
In this chapter, the concept of multithreading is introduced. Various examples are given to create new threads, giving priorities to threads etc.,
Media Attributions
- 13.1 Life cycle of a Thread