MultiThreading

Last Updated on August 26, 2023 by KnownSense

When we create an application, we write lines of code and logic is also correct. But still we wonder that our application is not upto the mark. One important factor that plays a vital role in any application development is its Performance.
However the performance of an app is not only related to the number of processors you have but how the logic is framed in a way so as to better utilize your CPU resources.
One of those important terminology for the same is Multithreading.

What is Multithreading?

Multithreading in java is a concept in which the application can create a small unit of tasks to execute in parallel. These small units of tasks here are known as threads.
 If you are working on a computer, it runs multiple applications and allocates processing power to them. A simple program runs in sequence and the code statements execute one by one. This is a single-threaded application. But, if the programming language supports creating multiple threads and passes them to the operating system to run in parallel, it’s called multithreading.
Multithreaded applications execute two or more threads run concurrently. Hence, it is also known as Concurrency in Java. Each thread runs parallel to each other. Multiple threads don’t allocate separate memory area, hence they save memory. Also, context switching between threads takes less time.

Benefits of Multithreading

Couple of benefits of Multithreading in an operating system are as follow

  1. Responsive: Multithreading in an interactive application enables a program to continue running even if a section is blocked or executing a lengthy process, increasing user responsiveness. For instance, a multithreaded web browser permits the user interaction in one thread while a video is loading in another thread. As a result, instead of waiting for the entire web page to load, the user can continue viewing a section of a web page.
  2. Resource Sharing: Processes can only share the resources only via two techniques such as:
    – Message Passing
    – Shared Memory
    The programmer must explicitly structure such strategies. On the other hand, by default, threads share the memory and resources of the process they belong to.
  3. Economy: Allocating memory and resources for process creation is an expensive procedure because it is a time and space-consuming task. Because threads share a memory with the process to which they belong, establishing and context switching threads is more cost-effective
  4. Scalable: The advantages of multi-programming become much more apparent in the case of multiprocessor architecture, when threads may execute in parallel on many processors. A single-threaded process could only run on one processor, despite the number of processors available. Multithreading on multiple CPU machines increases parallelism.
  5. Communication: Thread synchronization functions could be used to improve inter-process communication. Moreover, sharing huge amounts of data across multiple threads of execution inside the same address space provides extremely high-bandwidth, low-latency communication across various tasks within an application.

Before going deep now, let’s first understand one of the major concept here known as threads.

What is a thread and its life cycle?

A thread in Java is a lightweight process requiring fewer resources to create and share the process resources

  1. New: In this phase, the thread is created using class “Thread class”.It remains in this state till the program starts the thread. It is also known as thread born.
  2. Runnable: In this state, the instance of the thread is invoked with a start method. The thread control is given to scheduler to finish the execution. It depends on the scheduler, whether to run the thread.
  3. Running: When the thread starts executing, then the state is changed to “running” state. The scheduler selects one thread from the thread pool, and it starts executing in the application.
  4. Waiting: This is the state when a thread has to wait. As there multiple threads are running in the application, there is a need for synchronization between threads. Hence, one thread has to wait, till the other thread gets executed. Therefore, this state is referred as waiting state.
  5. Dead: This is the state when the thread is terminated. The thread is in running state and as soon as it completed processing it is in “dead state”.

Types of thread:
Java offers two types of threads: user threads and daemon threads. User threads are high-priority threads. The JVM will wait for any user thread to complete its task before terminating it. On the other hand, daemon threads are low-priority threads whose only role is to provide services to user threads

Creation of thread:

Thread thread = new Thread(new Runnable(){
    @Override
    public void run() {
    }
});
thread.start();

Above is a simple program to create a thread.
Once a thread is created and we need to start its execution, we can do so by calling the start() method.

Constructor of Thread class:

  • Thread()
  • Thread(String name)
  • Thread(Runnable r)
  • Thread(Runnable r,String name)

Some common methods of thread class:

Method
Description
start()This method starts the execution of the thread and JVM calls the run() method on the thread.
run()is used to perform action for a thread.
Sleep(int milliseconds)This method makes the thread sleep hence the thread’s execution will pause for milliseconds provided and after that, again the thread starts executing. But Sleep method does not release the lock on an object until it is interrupted. need not be called within Synchronized context.
wait()Wait method releases the lock on the object to let other objects execute till notify or notifyAll method is invoked by other thread on object. Wait method has to be called within the Synchronized context.
notify() & notifyAll()The notify() method wakes up a single thread that is waiting on that object’s monitor. The notifyAll() method wakes up all threads that are waiting on that object’s monitor.
getName()It returns the name of the thread.
setPriority(int newpriority)It changes the priority of the thread.
yield ()It causes current thread on halt and other threads to execute.
setName(String Name)changes the name of the thread.
 join(long miliseconds)waits for a thread to die for the specified miliseconds.
setDaemon(boolean b) marks the thread as daemon or user thread.

Difference between start and run in Java Thread is that when program calls start() method a new Thread is created and code inside run() method is executed in new Thread while if you call run() method directly no new Thread is created and code inside run() will execute on current Thread.

Implementation of Multithreading:
We can implement Multithreading in java in 2 ways:

  1. Extending Thread class
  2. Implementing Runnable interface

Example A. Extending thread class

class Thread1 extends Thread{
	public void run(){
		System.out.println(“My first thread”);
	}
}
class Thread2 extends Thread{
	public void run(){
		System.out.println(“My second thread”);
	}
}
class KnownsenseDemo {
	public static void main(String[] args){
		Thread1 t1 = new Thread1();
		Thread2 t2 = new Thread2();
		t1.start();
		t2.start();
	}
}

Output:
My first thread
My second thread
Explanation:
In the above example 2 different thread classes are created by extending the actual Thread Class.
Line 1-5 has code for the Thread1 class with only a print statement.
Similarly line 6-10 has code for Thread2 class with another print statement.
Line 11-18 is the actual implementation, where:
11 -> KnownsenseDemo class is created
12 -> main method/ entry point method which will be executed first as the application starts up
13 -> t1 is the object created for the Thread1 class.
14 -> t2 is the object created for the Thread2 class.
15 & 16 -> both the created thread objects are now started.

Example B: Implementing Runnable Interface:

 class Thread1 implements Runnable { 
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread 1 running");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
   }
}

class Thread2 implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread 2 running");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
    }
}

public class KnownsenseDemo { 
        public void main(String[] args) {
        Runnable obj1 = new Thread1();
        Runnable obj2 = new Thread2();
        Thread t1 = new Thread(obj1);
        Thread t2 = new Thread(obj2);
        t1.start();
        t2.start();
    }
}

Output:
Thread 2 running
Thread 1 running
Thread 2 running
Thread 1 running
Thread 1 running
Thread 1 running
Thread 2 running
Thread 1 running
Thread 2 running
Thread 2 running
Explanation:
In the above case 2 classes(Thread1 and Thread2) are created implementing the Runnable interface.
Both the classes override the run() method and iterated 5 times in a loop with a print statement. Also in the try block we have asked the thread to sleep for 1 sec each. In the main class i.e KnownsenseDemo which has the implementation logic. Firstly objects are created for our thread classes and then these objects are passed the the newly created instance of the thread class. Once the thread class objects executes the method start(), threads starts execution and waits for 1 sec and it happens for a loop of 5 times. Finally the output is as shown to you. The sequence of thread 1 and thread 2 is not fixed.

It is preferred to implement a Runnable interface instead of extending Thread class. As implementing Runnable makes your code loosely coupled as the code of thread is different from the class that assign job to the thread. It requires less memory as each thread created by implementing Runnable interface shares same object space hence, and also allows a class to inherit any other class.

Conclusion:
Finally we can see how to implement the multithreading in java.
Whenever you wanted to create threads, there are only two ways:

  1. Extending the thread class
  2. Implementing the interface which is runnable. Make sure to create an object of threads in which you have to pass the object of runnable

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to Top