Java Threads (original) (raw)

**Java threads are lightweight subprocesses, representing the smallest unit of execution with separate paths. The main advantage of multiple threads is efficiency (allowing multiple things at the same time). For example, in MS Word, one thread automatically formats the document while another thread is taking user input. Additionally, multithreading ensures quick response, as other threads can continue execution even if one gets stuck, keeping the application responsive.

Threads in a Shared Memory Environment in OS

Threads in a Shared Memory Environment in OS

As shown in the above diagram, a thread runs inside the process, and there will be context-based switching between threads. There can be multiple processes running in an OS, and each process can have multiple threads running simultaneously. The Multithreading concept is popularly applied in games, animation, etc.

Concept of Multitasking

To help users, the operating system provides users with the privilege of multitasking, where users can perform multiple actions simultaneously on the machine. This Multitasking can be enabled in two ways:

  1. Process-Based Multitasking
  2. Thread-Based Multitasking

**1. Process-Based Multitasking (Multiprocessing): In this type of multitasking, processes are heavyweight, and each process is allocated by a separate memory area and as the process is heavyweight the cost of communication between processes is high and it takes a long time for switching between processes as it involves actions such as loading, saving in registers, updating maps, lists, etc.

**2. Thread-Based Multitasking: As we discussed above, threads are provided with lightweight nature and share the same address space, and the cost of communication between threads is also low.

Life Cycle of Thread

During its lifetime, a thread transitions through several states, they are:

  1. New State
  2. Active State
  3. Waiting/Blocked State
  4. Timed Waiting State
  5. Terminated State

Life Cycle Of Thread

We can see the working of **different states in a Thread in the above diagram, let us know in detail each and every state:

Working of Thread States

**1. New State: By default, a thread will be in a new state, in this state, code has not yet started execution.

**2. Active State: A thread that is a new state by default gets transferred to Active state when it invokes the **start() method, this Active state contains **two sub-states namely:

**3. Waiting/Blocked State: If a thread is inactive but on a temporary time, then either it is a waiting or blocked state, for example, if there are two threads, T1 and T2 where T1 needs to communicate to the camera and the other thread T2 already using a camera to scan then T1 waits until T2 thread completes its work, at this state T1 is parked in waiting for the state, and in another scenario, the user called two Threads T2 and T3 with the same functionality and both had same time slice given by thread scheduler then both Threads T1, T2 is in a blocked state. When there are multiple threads parked in a Blocked/Waiting state thread scheduler clears queue by rejecting unwanted threads and allocating CPU on a priority basis.

**4. Timed Waiting State: Sometimes the longer duration of waiting for threads causes starvation, if we take an example like there are two threads T1, T2 waiting for CPU and T1 is undergoing a critical coding operation and if it does not exist the CPU until its operation gets executed then T2 will be exposed to longer waiting with undetermined certainty, In order to avoid this starvation situation, we had timed waiting for the state to avoid that kind of scenario as in timed waiting, each thread has a time period for which sleep() method is invoked and after the time expires the threads starts executing its task.

**5. Terminated State: A thread will be in terminated state, due to the below reasons:

Java Main Thread

As we are familiar, we create main method in each and every Java program, which acts as an entry point for the code to get executed by JVM, Similarly in this multithreading concept, each program has one main thread which was provided by default by JVM, hence whenever a program is being created in java, JVM provides the main thread for its execution.

How to Create Threads in Java?

We can create threads in java using two ways, namely :

1. By Extending Thread Class

We can run threads in Java by using thread class, which provides constructors and methods for creating and performing operations on a thread, which extends a thread class that can implement runnable interface. We use the following constructors for creating the Thread:

**Example:

Java `

//Driver Code Starts{ import java.io.; import java.util.; //Driver Code Ends }

class MyThread extends Thread { // initiated run method for Thread public void run() { String str = "Thread Started Running..."; System.out.println(str); } }

//Driver Code Starts{ public class Geeks { public static void main(String args[]) { MyThread t1 = new MyThread(); t1.start(); } } //Driver Code Ends }

`

Output

Thread Started Running...

2. Using Runnable Interface

**Example:

Java `

//Driver Code Starts{ import java.io.; import java.util.; //Driver Code Ends }

class MyThread implements Runnable {
// Method to start Thread public void run() { String str = "Thread is Running Successfully"; System.out.println(str); }

}

//Driver Code Starts{ public class Geeks { public static void main(String[] args) { MyThread g1 = new MyThread();

    // initializing Thread Object
    Thread t1 = new Thread(g1);
    
      // Running Thread
      t1.start();
}

} //Driver Code Ends }

`

Output

Thread is Running Successfully

Running Threads in Java

There are two methods used for running Threads in Java:

**Example: Running a thread using start() Method

Java `

// Running the Threads in Java import java.io.; import java.util.;

// Method 1 - Thread Class class ThreadImpl extends Thread { // Method to start Thread @Override public void run() { String str = "Thread Class Implementation Thread" + " is Running Successfully"; System.out.println(str); } }

// Method 2 - Runnable Interface class RunnableThread implements Runnable {
// Method to start Thread @Override public void run() { String str = "Runnable Interface Implementation Thread" + " is Running Successfully"; System.out.println(str); }

}

public class Geeks { public static void main(String[] args) { // Method 1 - Thread Class ThreadImpl t1 = new ThreadImpl(); t1.start();

      // Method 2 - Runnable Interface
    RunnableThread g2 = new RunnableThread();
    Thread t2 = new Thread(g2);
      t2.start();
      
      // Wait for both threads to finish before printing the final result
      try {
          // Ensures t1 finishes before proceeding
          t1.join();  
          
          // Ensures t2 finishes before proceeding
          t2.join();  
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
}

}

`

Output

Thread Class Implementation Thread is Running Successfully Runnable Interface Implementation Thread is Running Successfully

The common mistake is starting a thread using run() instead of start() method.

Thread myThread = new Thread(MyRunnable());
myThread.run(); //should be start();

The run() method is not called by the thread you created. Instead, it is called by the thread that created the myThread.

**Must Read start() vs run() in Java article, to explore more about the topic.

Checking States of Thread in Java

Let us see the working of thread states by implementing them on Threads t1 and t2.

**Example:

Java `

class Geeks implements Runnable { public void run() { // implementing try-catch Block to set sleep state // for inactive thread try { Thread.sleep(102); } catch (InterruptedException i1) { i1.printStackTrace(); }

    System.out.println("The state for t1 after it invoked join method() on thread t2: "
                        + " " + ThreadState.t1.getState());

    // implementing try-catch block
    try {
        Thread.sleep(202);
    } catch (InterruptedException i2) {
        i2.printStackTrace();
    }
}

}

// Creation of ThreadState class public class ThreadState implements Runnable { // t1 static to access it in other classes public static Thread t1; public static ThreadState o1;

public void run() {

    Geeks geeks = new Geeks();
    Thread t2 = new Thread(geeks);

    // Thread is created and its in new state
    t2.start();

    // Now t2 is moved to runnable state
    System.out.println("State of t2 Thread, post-calling of start() method is: "
                        + " " + t2.getState());

    // Create a try-catch block to set t1
      // in waiting state
    try {
        Thread.sleep(202);
    } 
      catch (InterruptedException i2) {
        i2.printStackTrace();
    }

    System.out.println("State of Thread t2 after invoking to method sleep() is:"
                        + " " + t2.getState());

    try {
        t2.join();
        System.out.println("State of Thread t2 after join() is: " + t2.getState());
    } 
      catch (InterruptedException i3) {
        i3.printStackTrace();
    }

    System.out.println("State of Thread t1 after completing the execution is: "
                        + " " + t1.getState());
}

public static void main(String args[]){
    o1 = new ThreadState();
    t1 = new Thread(o1);

    System.out.println("Post-spanning, state of t1 is: " + t1.getState());

    // lets invoke start() method on t1
    t1.start();

    // Now, Thread t1 is moved to runnable state
    System.out.println("Post invoking of start() method, state of t1 is: "
                        + " " + t1.getState());
}

}

`

**Output:

Output

Concurrency Problems

**Solution of the Problem: Synchronization , Locks , Atomic Variables , Thread-safe Collections , Avoiding Deadlocks , Thread Pools and Using volatile Keyword.

These are the points we can use to avoid concurrency problems in our program or application.

Let us see how we can avoid it.

Java `

// Concurrent Problems Solution class Counter {

// Shared resource
private int count = 0;

// Synchronized method to ensure
// thread-safe increment
public synchronized void increment()
{

    // Increment the counter
    count++;
}

// Method to get the current count
public int getCount() { return count; }

}

class CounterThread extends Thread { private Counter counter;

// Constructor to initialize the
// counter
public CounterThread(Counter counter)
{
    this.counter = counter;
}

// Override the run method to
// increment the counter
@Override public void run()
{
      System.out.println("Running the Thread");
    
      // Increment the counter
    for (int i = 0; i < 1000; i++) {
        counter.increment();
    }
}

}

public class Geeks { public static void main(String[] args) {

    // Create a shared Counter object
    Counter counter = new Counter();

    // Create multiple threads that will
    // increment the counter
    Thread t1 = new CounterThread(counter);
    Thread t2 = new CounterThread(counter);
    Thread t3 = new CounterThread(counter);

    // Start the threads
    t1.start();
    t2.start();
    t3.start();

    // Wait for all threads to finish
    try {
        t1.join();
        t2.join();
        t3.join();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }

    // Print the final count
    System.out.println("Final count: "
                       + counter.getCount());
}

}

`

Output

Running the Thread Running the Thread Running the Thread Final count: 3000

Explanation of the above Program:

Advantages of Creating Threads