Understanding The Concepts Behind Java Multithreading
Resources play an essential role in real as well as in the digital world. When we talk about the digital world, resources include CPU storage, processing, and memory. These three aspects affect the performance of a program collectively. The speed of execution a code combined with the memory and space utilization is a measure of the efficiency of the code. Java is a multi-threading programming language. As the operating systems, Java offers the feature of multithreading to improve the performance and efficiency of your code.
As the term suggests, multithreading deals with multiple threads. Threads in programming terms are nothing but lightweight processes. Running multiple threads at the same time improves the speed of execution and also helps in the efficient utilization of resources available at hand.
Multithreading Simplified
Even if you are new to the field of development you might have at some point used a phone or computer with an operating system. You might be familiar with the concept of multitasking. The process of multitasking allows you to work with multiple applications and operations at the same time. You browse the internet with music playing in the background or download a movie while drafting a document. All these are simple examples of multitasking. Multithreading extends the concept of multitasking to the execution of code in Java. Rather than running multiple processes simultaneously, the Java compiler executes parts of the same code concurrently.
To simplify the concept of multitasking further, it is when multiple processes share common resources such as CPU to run simultaneously. Likewise, multithreading runs parts of the same code independent to each other to achieve parallelism.
What is a Thread in Programming?
As mentioned earlier, a thread is a lightweight process. To simplify threads further a thread is a sub-process that can run independently in a code. It is the smallest part of the program that can run concurrently with other threads in the code. In addition to this, each thread or sub-process has a separate execution path. The different execution path allows a sub-process to be independent of other threads in the code. However, all these threads share a shared memory space while executing. When more than one such threads run simultaneously, it is known as multithreading.
Each thread in Java has a particular lifecycle during the execution of the program. It can be in one of the following states during the lifecycle:
New
A thread begins its lifecycle in the new state. It remains in this state until the program initiates its execution.
Runnable
When the thread starts executing its task in the Java Virtual Machine (JVM) it is said to be in the runnable state.
Waiting
A thread transitions to the waiting state while waiting for another thread to complete its task. The thread returns to the runnable state only after the thread in control signals it to resume execution.
Timed waiting
A thread transitions into the timed waiting state from the runnable state for a fixed interval of time. It goes back to the runnable state after the expiration of the defined time or after an event that the thread was waiting for occurs.
Terminated
When the thread completes execution of its particular task, it enters the terminated or dead state.
Implementing Multithreading in Java
There are two ways to implement multithreading in Java. Java provides an inbuilt interface and a class to perform these two ways.
Using the Runnable interface
If you wish to implement a class as a thread, you can achieve this by implementing the Runnable interface. When implementing the Runnable interface, you will be prompted to define the run function declared in the interface. The method signature of the run function is public void run (). The method provides an entry point to the thread. In addition to this, you need to write the underlying logic of the task that is to be performed by the thread.
To implement the class as a thread, you will need to use a constructor of the Thread class. The signature of the particular constructor used to instantiate or create a Thread object is Thread (Runnable thread, String thread name). The thread object of type runnable is the object of the class that implements the Runnable interface. The second parameter assigns a user-defined name to the thread and is optional.
When the thread is created, you need to call the void start() function on the thread object. The start() function implicitly calls the run function to execute the thread logic.
That might look like this:
class RunThread implements Runnable {
public void run() {
System.out.println(“Your thread is running, you better catch it.”)
}
}
To create a thread, you would do something like this:
public static void main(String[] args) {
RunThread myThread = new RunThread();
Thread theThread = new Thread(myThread);
}
Extending the Thread class
The second way to create a thread in java is by extending the Thread class. You will need to override the run method in the Thread class to implement the business logic unique to that thread. You then need to create the object of the class and call the start function on it.
That might look like this:
class MultiThread extends Thread {
public void run(){
System.out.println( “My thread is running, you better go catch it.”);
}
}
To run that thread in a main method, you might do something like this:
public static void main(String args[]){
MultiThread myThread = new MultiThread();
myThread.start();
}
Operations on Threads
Java provides a list of operations that can be performed on the threads to control the flow of a multithreaded program. Apart from the start () and run () functions, here is a list of some of the essential functions that are frequently used on threads:
public final void setName (String name)
The method changes the name of the thread to a user-defined name.
public final void setPriority (int priority)
The function sets the priority of the thread object on which it is invoked. The value of the priority ranges from one to ten with one being the lowest and ten being the highest.
public void suspend()
The method puts a thread into a suspended state. The thread can resume execution using the resume () method.
public void stop()
The stop() method terminates the thread and stops its execution completely.
public void wait()
The wait method causes the current thread to wait until another thread invokes the notify function. This method is used to synchronize the working of threads when two or more threads try to access a common shared resource.
public void notify()
The function wakes up a thread that is waiting to work on a resource that was held by the thread object on which it is invoked.
public static void yield()
The yield method causes a currently running thread to suspend execution to allow other threads of the same priority to run.
public static void sleep (long millisec)
The method changes the state of the thread to the blocked state for the period defined in the parameter of the function.
There are many more functions that can be used with threads as per requirements to control the complete flow of the multithreaded program in Java. You can find the complete list of functions on the official Oracle site.
Thread Synchronization
When you run multiple threads, there may arise a situation when two or more threads try to access a shared resource at the same time. This simultaneous access of resources by threads can often lead to unforeseen and erroneous situations. For example, two threads may try and write to the same file concurrently during their execution. Thus, the simultaneous access of the file can cause inconsistency of data or corrupt the file altogether.
Therefore, a need arises to synchronize the working of threads to restrict one thread using a particular resource at a time. Java allows the developer to do so using the concept of monitors. The monitor allows only one thread at a time to access the particular resource.
To implement synchronization in Java the shared or common resources need to be placed in a synchronized block.
The syntax for declaring such resources is:
synchronized(object)
{
The braces contain the resources that are common to the threads.
}
Here the parameter object indicates the reference to the object that holds the lock on the monitor represented by the synchronized block.
Advantages of Using Multithreading in Java
The laptops and computers these days are equipped with multiple processors. However, while coding, we do not pay much attention to the hardware of the system. Thus, in doing so, we do not utilize the full potential of the machine. Multithreading not only allows us to increase parallelism in coding but also helps us take advantage of the multiprocessor architecture of the system.
Apart from the main advantage of increased parallelism and speed of execution here are some other significant benefits of multithreading:
Throughput
Throughput is a measure of the efficiency of the code. The term defines the number of tasks executed successfully in a specific period. When using multithreading in Java, the throughput of the code is substantially increased. The reason behind increased throughput is the concurrent execution of operations and handling of I/O (input-output) requests.
Responsiveness
Responsiveness is the quality of an application to react to a particular action or command. When you employ multithreading in your code, you drastically increase the application responsiveness. The increased responsiveness is a result of multiple threads handling background tasks simultaneously. In the absence of multithreading, the whole application comes to a standstill until the completion of a single task.
Minimum usage of system resources
The process of multithreading reduces the utilization of system resources. The creation, management, and execution of threads require less overhead than a traditional process. Thus, this puts a minimum impact on the system resources.
Simplified program structure
By employing multithreading in your code, you can simplify your program structure. You can divide and separate each task in an application into a thread that executes an independent task. In this way, you can manage server-class and other complex multimedia applications. The division of code into threads not only simplifies the structure of the code but also enables you to cater to a wide variety of user demands.
Efficient communication
Communication is a key when performing complex and considerable tasks in programming. The processes need to communicate to meet a single objective. However, this communication overhead can reduce the speed of execution drastically. But when using threads, the sub-processes share a common memory space that enables sharing of large amounts of data, low-latency communication, and high bandwidth. All these results in low communication overhead and high speed of execution.
Now that you are aware of what and why is multithreading necessary in programming, you can think of ways to employ it on complex pieces of code to simplify the structure and increase the efficiency of your code. With this, we conclude the topic of multithreading in java, and you are a step closer to mastering not only Java but the working of the computer as a whole.
You May Also Need
[amazon box=”178847306X,0596007825,1260440230″ grid=”3″]