Thread in Java14 min read

What is a thread in Java?

A Thread in Java program consists of two or more components that can operate in parallel. Multi-threading is built-in to Java, unlike many other programming languages. Two or more portions of a program can execute at the same time. A thread is a component of such a program. Each thread functions as a mini-program.

  • Multi-threading is a type of multitasking that is more advanced. Although the individual threads run independently, they can communicate with one another by sharing variables.
  • Programming with threads is called parallel programming as several threads can run in parallel.
  • A thread in Java is just an object of type Thread.
  • Thread is a Java class that comes from java.lang package.
  • A thread object must have a run(); procedure to execute.
  • Although each thread is processed in order, the relative order of exceptions in a distinct control thread is uncertain.
  • Java.lang contains the central multi-threading class.
  • When deciding when to transition from one running thread to the next, priority is employed.
  • The priority values range from one to ten. The main thread’s name is defaulted to main, and its priority is set to 5.
  • The main thread is significant since it is the thread from which other “child” threads will emerge, as well as the thread that will complete the execution. The program ends when the main thread stops.
  • Thread priorities are integers that represent a thread’s priority in relation to another thread’s priority.
  • The Thread class and the Runnable Interface have two often used methods.
    • getName() returns the thread’s name.
    • setName() to change the thread’s internal name.
  • When the application starts, the main thread is automatically generated. By invoking currentThread on a Thread object, you can control this main thread.

Features of Thread

  • A thread can be active at any time.
  • A Thread can start running as soon as it receives CPU time.
  • The activity of a running thread can be temporarily interrupted by suspending it. Threads can be found in a variety of states.
  • A suspended thread can then be resumed when allowing it to continue the remaining activity.
  • A Thread can be terminated at any time, which halts its execution immediately. Note that after terminating a thread, it cannot be resumed.

Java Thread Model

  • For many things, the Java run-time system relies on threads, and all of the class libraries are built with multithreading in mind. Java takes advantage of threads to make the entire environment asynchronous. This helps to reduce inefficiencies by reducing CPU cycles from being wasted.
  • In comparison to its equivalent, the value of a multithreaded environment is best known. In single-threaded systems, an event loop with polling is used. A single control thread operates in an infinite loop in this architecture, querying a single event queue to determine what to do next.
  • The event loop passes control to the appropriate event handler once this mechanism returns with, for instance, a signal that a network file is ready to be read. Nothing else can happen in the system until this event handler returns. This consumes CPU resources. It can also lead to one component of software taking control of the system and stopping the processing of any other events.
  • When a thread blocks because it is waiting for a resource in a single-threaded environment, the entire program stops operating.
  • The main loop/polling technique is abolished thanks to Java’s multithreading. One thread can stall while other sections of your application continue to run.
  • Threads can be found in a variety of states. It is possible for a thread to be active. As soon as it receives CPU time, it will be ready to run. The activity of a running thread can be temporarily halted by suspending it. A thread that has been suspended can then be resumed, allowing it to continue where it left off. When a thread is waiting for a resource, it can become blocked.
  • A thread can be terminated at any time, halting its operation immediately. A thread cannot be resumed once it has been terminated.

Main Thread

  • When a Java application starts, one thread is instantly active. This is commonly referred to as the main thread of your program because it is the first one to run when your application starts.
    The main thread is significant for two reasons: first, it connects all of the other threads.
    It is from this thread that other “child” threads will emerge.
  • Because it conducts numerous shutdown procedures, it is frequently the final thread to conclude execution.
  • Although your program’s main thread is established automatically when it runs, it can be manipulated using a Thread object. To do so, call the function currentThread(), which is a public static member of Thread, to get a reference to it.
  • Its general form is shown below:
static Thread currentThread()
  • This method returns a reference to the thread in which it is called. Once you have a reference to the main thread, you can control it just like any other thread.

Creating a Thread

  • In the broadest sense, a thread is created by instantiating a Thread object. Objects with a run method are used to implement threads (). Any thread’s heart and soul is the run() method. It is the only way to implement the thread’s behavior and makes up the complete body of the thread.
  • An object of the concerned thread should call the run() function. This can be accomplished by first generating the thread and then launching it using the start() thread method.

A new thread can be created in two ways:

  1. By creating a thread class: Define a class that extends the Thread class and override its run() method with the code required by the thread.
  2. By creating a thread using Runnable Interface: Define a class that implements Runnable interface. The Runnable interface has only one method, run(), that is to be defined in the method with the code to be executed by the thread.
  • The approach to take is determined by the requirements of the class we’re developing. We have no choice but to implement the Runnable interface if we need to extend another class because Java classes cannot have two superclasses.

Creating a thread by creating a thread class

  • This is how the Thread class can be extended: class Thread is extended by MyThread.
  • MyThread is a new type of thread that we have now. The run() method is implemented.
  • The class MyThread has inherited the run() function. In order to implement the code that will be executed by our thread, we must override this method.

The basic implementation of run() will look like this:

public void run() {
  ......
  ......  
}

When we start the new thread, Java calls the thread’s run() method, so it is the run() where all the action takes place.

Example:

import java.io.*;
class Hiberstack extends Thread {
  public void run() {
    System.out.print("Welcome to Hiberstack.");
  }
  
  public static void main(String[] args) {
    Hiberstack hiber = new Hiberstack(); // creating thread
    hiber.start(); // starting thread
  }
}

Output:

Welcome to Hiberstack.

Creating thread using Runnable Interface

  • Creating a class that implements a Runnable interface can create a thread.
  • run () method is necessary to implement Runnable.
  • Inside the run () we constitute the new thread.
  • Like the main thread, run () can call other methods, use other classes and declare variables.
  • When we implement Runnable, it instantiates an object of type Thread from within that class.
  • The following example describes how to implement the Runnable interface.
class Multi3 implements Runnable {
  public void run() {  
    System.out.println("thread is running...");  
  }  
  
  public static void main(String[] args) {  
    Multi3 m1=new Multi3();  
    Thread t1 =new Thread(m1);  
    t1.start();  
  }  
}  

Output:

thread is running...

Extending Thread

We can make our class runnable as a thread by extending the class java.lang.Thread. This gives us access to all the thread methods directly. It includes the following steps:

  1. Declare the class as extending the Thread class.
  2. Implement the run() method that is responsible for executing the sequence of code that the thread will execute. the thread execution.
  3. Create a thread object and call the start() method to initiate.
class Multi extends Thread {  
  public void run() {  
    System.out.println("thread is running...");  
  }  
  public static void main(String[] args) {  
    Multi t1=new Multi();
    t1.start();  
  }  
}  

Output:

thread is running...

Starting New Thread

To actually create and run an instance of our thread class, we must write the following:

MyThread aThread = new MyThread();
aThread.start(); // invokes run() method

The first line instantiates a new object of class MyThread. Note that this statement just creates the object. The thread that will run this object is not yet running. The thread is in a newborn state.


Creating Multiple Threads

We have been using only two threads: the main thread and one child thread. However, your program can spawn as many threads as it needs.

Example:

class MyThread implements Runnable {
  String name;
  Thread t;
  MyThread(String thread) {
    name = thread; 
    t = new Thread(this, name);
    System.out.println("New thread: " + t);
    t.start();
  }
  
  public void run() {
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println(name + ": " + i);
        Thread.sleep(1000);
      }
    }catch (InterruptedException e) {
      System.out.println(name + "Interrupted");
    }
    System.out.println(name + " exiting.");
  }
}

public class MultiThread {
  public static void main(String[] args) {
    new MyThread("One");
    new MyThread("Two");
    new NewThread("Three");
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      System.out.println("Main thread Interrupted");
    }
    System.out.println("Main thread exiting.");
  }
}

Output:

New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Three: 3
Two: 3
One: 2
Three: 2
Two: 2
One: 1
Three: 1
Two: 1
One exiting.
Two exiting.
Three exiting.
Main thread exiting.

Life Cycle of a Thread in Java

During the lifetime of a thread, there are many states it can enter. They include:

  1. Newborn state
  2. Runnable state
  3. Running state
  4. Blocked state
  5. Dead state

A thread is always in one of these five states. It can move from one state to another in a variety of ways as shown in the following figure:

Life Cycle of a Thread in Java
Life cycle of thread

1. Newborn State

When we create a thread object, the thread is born and is said to be in the newborn state. The thread is not yet scheduled for running. At this stage, we can do only one of the following things with it:

  • Schedule it for running using start() method.
  • Kill it using stop() method.
Newborn State of thread in Java
Newborn State

2. Runnable State

  • The thread is in the runnable state, which indicates it is ready to execute and is waiting for the processor to become available.
    That is, the thread has been added to the list of threads awaiting execution.
  • If all threads have equal priority, then they are given a time slot for execution in a round fashion, i.e., first come, first serve manner.
  • The thread that relinquishes control rejoins the queue at the bottom and waits its turn once more. Time-slicing is the process of assigning time to threads.
  • However, if we want to thread to relinquish control to another thread to equal priority before its turn comes, we can do so by using the yield() method.
Runnable State

3. Running State

Running means that the processor has given its time to the thread for its execution. The thread runs until it relinquishes control on its own or it is preempted by a higher priority thread. A running thread may relinquish its control in one of the following situations.

  • It has been suspended using suspend() method. A suspended thread can be revived by using the resume() method. This approach is useful when we want to suspend a thread for some time due to a certain reason, but do not want to kill it.
Running State of thread in Java
  • It has been made to sleep. We can put the thread to sleep for a specified time period using the method sleep(time) where time is in milliseconds. This means that the thread is out of the queue during this time period. The thread re-enters the runnable state as soon as this time period is elapsed.
  • It has been told to wait until some event occurs. This is done using the wait() method. This thread can be scheduled to run again using the notify() method.

4. Blocked State

  • When a thread is stopped from entering the runnable state and then the running state, it is said to be blocked.
  • When a thread is suspended, asleep, or waiting to meet certain prerequisites, this occurs.
  • A thread that has been blocked is regarded as “not runnable,” but not “dead,” and hence fully qualified to run again.

5. Dead State

  • There is a life cycle for every thread. When the run() method of a running thread has been completed, the thread’s life comes to an end.
  • It’s the end of the world as we know it. We can, however, kill it by delivering the stop message to it at any time, causing it to die prematurely.
  • A thread can be killed as soon it is born, or while it is running, or even when it is in “not runnable” condition.

Example:

import java.io.*;
public class Thread1 {
  public static void main(String[] args) { 
    Thread t = Thread.currentThread(); 
    System.out.println("Current thread :" + t); 
    try { 
      for (int n = 1; n <= 5; n++) { 
        System.out.println(n);
        Thread.sleep (1000); 
      } 
    } 
    catch (InterruptedException e) {
      System.out.println("Main thread interrupted"); 
    } 
  } 
}

Synchronizing threads

  • When a program has more threads, it is possible that two or more threads may require sharing the same resource.
  • Hence there must be some way to ensure that the resource is used only one thread at a time. This process is called synchronization.
  • Exclusive resource access is implemented in Java via synchronized methods and synchronized statements.
  • In order to run a synchronized method in an object lock, there is a rule. If another thread is holding the lock, the second thread must wait until the first thread releases the lock.
  • If all access to a shared resource takes place inside methods that are synchronized on the same object, then each thread that accesses the resource has exclusive access.
  • Exclusive resource access is implemented in Java via synchronized methods and synchronized statements.
  • Every object has a lock.
  • A monitor is an object, which is used as an exclusive lock. With every, an implicit monitor is associated.
  • At any given time, only one thread can own a monitor. A thread is considered to be entered when it gains a block.

Example

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String[] args) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Output:

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Share:

Leave a Reply