What is Multithreading in Java

The demand for more responsive and efficient software applications is constantly growing in the ever-changing landscape of modern computing.

Multithreading is a crucial concept in computer science and software development that provides a great opportunity to fully utilize today’s hardware capabilities. Developers can enhance the speed and responsiveness of programs by utilizing multithreading. It does this by enabling them to run several threads simultaneously within one process.

Java Multithreading

  • Multithreading is the process of running several tasks in a program at once. It’s like a few “mini programs” or threads, each doing its own thing in the larger program at the same time. Multithreading is also known as Thread-based multitasking.
  • Thread: A thread is the smallest lightweight process. The collection of sub-processes is known as thread.

What is Multithreading

Life cycle of Thread in Java:

1. New: Whenever a thread is created, it enters into a “New” state. In this state, the thread has been instantiated but has not yet started executing the run method.

2. Runnable: After calling the start() method on the thread object, the thread moves to the “Runnable” state.

3. Running: When the scheduler selects the thread for execution, it enters the “Running” state. In this state, the thread’s run method is actively executing on the CPU.

4. Blocked: A thread can transition to the “Blocked” state if it is waiting for a particular condition, such as I/O or a lock, to be satisfied. While blocked, the thread is not eligible for execution until the condition is met.

5. Waiting: A thread enters the “Waiting” state if it waits indefinitely for another thread to perform a specific action, like notifying it using the wait() method.

6. Timed Waiting: Similar to the “Waiting” state, a thread enters the “Timed Waiting” state when it waits for a specific time period using methods like sleep() or waits (timeout).

7. Terminated: When a thread completes its execution or is terminated prematurely, it enters the “Terminated” state. A thread can reach this state after successfully completing the run method or if an uncaught exception occurs within the thread.

Terminated

Single Thread Vs Multi Thread:

Single Thread Multi Thread
A single-threaded program has only one sequence of instructions executing at any given time Multi-threading allows a program to have multiple threads of execution within a single process
If one part of the program is blocked, the entire program is blocked It’s important to manage synchronization and avoid race conditions.
Well-suited for simple applications or tasks that don’t require simultaneous processing Well-suited for tasks that can be parallelized, like I/O bound

Commonly used Thread methods:

        S.No           

                       Method

         

          Description

            

1.

           

                         start()

A thread is started using the start() function, which causes it to begin running simultaneously with the other threads in the same process.
2.                           run() It contains the code that the thread will execute. 
3.             

                         join()

It is used when you want to ensure that a specific thread finishes its execution before continuing with the main thread or another thread.
4.     

                 sleep(long millis)

It makes the thread sleep for the specified number of milliseconds. 
5.       

                  currentThread()

It allows access and manipulation of the properties of the current thread.
6.         

                       isAlive()

It checks if a thread is still alive (i.e., has not been terminated). 

Working of MultiThreading with Two Threads:

The program will create and manage two separate threads of execution executing concurrently as part of a Multithreading operation with two Threads. Each Thread represents an independent sequence of instructions that can perform its tasks simultaneously with the other Thread.

We’ll take a look at the working of multithreading with two threads:

Step 1: Creating Threads

Two threads are created by the application. This may be done by either implementing the Runnable interface or by extending the Thread class and giving the instance to the Thread constructor.

Example:

class MyThread extends Thread{
   public void run(){
      //code to be executed by the thread
   }
}
class MyRunnable implements Runnable{
   public void run(){
       // Code that a thread is to execute
   }
}
public class Main {
   public static void main(String[] args){
       MyRunnable r=new MyRunnable();
       MyThread t = new MyThread();
         }
}

Step 2: Starting Threads

Once the threads are created, they are not yet executed. To start the execution of the threads, the program calls the start() method on each thread.

Example:

t.start();
t1.start();

Step 3: Concurrent Execution

The two Threads will be running simultaneously once they are started. The Java Virtual Machine manages to schedule threads at the available CPU cores. The order of execution between the two Threads is not definite and depends on the scheduler of the operating system.

Example:

class MyThread extends Thread{
    public void run(){
       for(int i=1;i<4;i++){
           System.out.println(Thread.currentThread().getName()+" "+i);
       }
    }
}
class MyRunnable implements Runnable{
    public void run(){
        for(int i=1;i<4;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException{
        MyThread t = new MyThread();
        MyRunnable r=new MyRunnable();
        Thread t1=new Thread(r);
        t.start();
        t1.start();
        t.setName("TechVidvan-Thread1");//setname() is used to set name for the thread
        t1.setName("TechVidvan-Thread2");
    }
}

Step 4: Thread Termination

If you do not explicitly terminate them, then the Threads will continue to execute until they have reached the end of their run() method or run into an exception. In this example, each Thread runs a loop three times and prints a message, so they will terminate after printing the messages three times.

Output:

TechVidvan-Thread1 1
TechVidvan-Thread1 2
TechVidvan-Thread1 3
TechVidvan-Thread2 1
TechVidvan-Thread2 2
TechVidvan-Thread2 3

The output may vary due to the non-deterministic nature of thread scheduling.

Key Advantages of Multithreading:

  • Improved Responsiveness: Multithreading allows programs to remain responsive even when performing time-consuming tasks.
  • Increased Performance: Multithreading can take advantage of modern multi-core processors, where each core can handle a separate thread.
  • Load Balancing: In certain scenarios, multithreading can help distribute the workload across multiple threads, ensuring that resources are utilized more evenly and efficiently.
  • Simplified Program Structure: Multithreading allows you to organize the code more effectively by assigning each task to a separate thread, reducing complexity and improving maintainability.
  • Real-time Processing: In real-time applications, such as audio/video streaming or gaming, multithreading is essential for achieving smooth and continuous processing, as delays can lead to noticeable lags or stutters.

Applications of Multithreading:

  • User Interfaces: Multithreading is widely used in graphical user interfaces (GUIs) to keep the interface responsive while executing time-consuming tasks in the background.
  • Web Servers: Web servers handle multiple client requests simultaneously. Multithreading enables concurrent processing of incoming requests, allowing the server to handle multiple connections.
  • Multimedia Applications: Multimedia applications, such as video players, audio players, and image processing software, often employ multithreading to ensure smooth playback.
  • Gaming: Multithreading helps distribute game-related tasks, such as physics simulations, AI computations, and rendering, across multiple threads to enhance performance.
  • Database Management Systems: Multithreading is used in database systems to handle multiple queries and transactions concurrently, allowing efficient management of concurrent database access.

Limitation of Multithreading:

  • Complexity: Multithreading introduces additional complexity to the code, as multiple threads may access shared resources concurrently.
  • Deadlocks: If two or more threads fail to work together properly, a deadlock may occur. This can lead to a system freeze, where no threads can progress.
  • Race Conditions: Race conditions happen when multiple threads attempt to modify shared data simultaneously, leading to an unpredictable and erroneous behavior.
  • Debugging Difficulties: Debugging multithreaded applications can be challenging due to the non-deterministic nature of thread scheduling.
  • Scalability: While multithreading can improve performance on multi-core processors, it may not scale linearly with the number of cores.

Summary

In conclusion, multithreading and the management of threads form an indispensable aspect of modern software development. By utilizing threads effectively, developers can create powerful applications that utilize hardware resources efficiently, deliver enhanced performance, and maintain responsiveness even during resource-intensive operations. A strong grasp of multithreading, threads, and their life cycle empowers developers to build robust, high-performance applications that meet the ever-increasing demands of the technology-driven world.