Using Threads in Java


Reading time: 30 minutes

Java is a multi-threaded programming language which means we can develop multi-threaded program using Java. A multi-threaded program contains two or more parts that can run concurrently and each part can handle a different task at the same time making optimal use of the available resources specially when our computer has multiple CPUs.

By definition, multitasking is when multiple processes share common processing resources such as a CPU. Multi-threading extends the idea of multitasking into applications where we can subdivide specific operations within a single application into individual threads. Each of the threads can run in parallel. The OS divides processing time not only among different applications, but also among each thread within an application.

Multi-threading enables us to write in a way where multiple activities can proceed concurrently in the same program.

In other words, a thread is actually a lightweight process. Unlike many other computer languages, Java provides built-in support for multithreaded programming. A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called thread and each thread defines a separate path of execution. Thus, multithreading is a specialized form of multitasking.

So, we can summarize the threads in the following way :-

  • A thread is a facility to allow multiple activities within a single process.
  • Its Referred to as a lightweight process.
  • A thread is a series of executed statements.
  • Each thread has its own program counter, stack and local variables.
  • A thread is a nested sequence of method calls.
  • Its shares memory, files and per-process state.

The Java Thread Model :

A thread goes through various stages in its life cycle. For example, a thread is born, started, runs, and then dies. The following diagram shows the complete life cycle of a thread.

life-cycle-of-thread-1

Following are the stages of the life cycle −

  1. New − A new thread begins its life cycle in the new state. It remains in this state until the program starts the thread. It is also referred to as a born thread.

  2. Runnable − After a newly born thread is started, the thread becomes runnable. A thread in this state is considered to be executing its task.

  3. Waiting − Sometimes, a thread transitions to the waiting state while the thread waits for another thread to perform a task. A thread transitions back to the runnable state only when another thread signals the waiting thread to continue executing.

  4. Timed Waiting − A runnable thread can enter the timed waiting state for a specified interval of time. A thread in this state transitions back to the runnable state when that time interval expires or when the event it is waiting for occurs.

  5. Terminated (Dead) − A runnable thread enters the terminated state when it completes its task or otherwise terminates.

Thread Priorities

Every Java thread has a priority that helps the operating system determine the order in which threads are scheduled.
Java thread priorities are in the range between MIN_PRIORITY (a constant of 1) and MAX_PRIORITY (a constant of 10).
By default, every thread is given priority NORM_PRIORITY (a constant of 5).
Threads with higher priority are more important to a program and should be allocated processor time before lower-priority threads. However, thread priorities cannot guarantee the order in which threads execute and are very much platform dependent.

Creating Thread

There are two ways to create a thread:

  1. By extending Thread class
  2. By implementing Runnable interface.

1. By Runnable interface:

Step 1 :

As a first step, you need to implement a run() method provided by a Runnable interface. This method provides an entry point for the thread and you will put your complete business logic inside this method. Following is a simple syntax of the run() method −

        public void run( )

Step 2 :

As a second step, you will instantiate a Thread object using the following constructor −

        Thread(Runnable threadObj, String threadName);

Where, threadObj is an instance of a class that implements the Runnable interface and threadName is the name given to the new thread.

Step 3 :

Once a Thread object is created, you can start it by calling start() method, which executes a call to run( ) method. Following is a simple syntax of start() method −

        void start();

Example :

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      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[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

Output :

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

2. By Thread Class :

This approach provides more flexibility in handling multiple threads created using available methods in Thread class.

Step 1 :

here, we need to override run( ) method available in Thread class. This method provides an entry point for the thread and you will put your complete business logic inside this method. Following is a simple syntax of run() method −

public void run( )

Step 2 :

Once Thread object is created, you can start it by calling start() method, which executes a call to run( ) method. Following is a simple syntax of start() method −

void start( );

Example :

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      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[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

Output :

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Thread class provide constructors and methods to create and perform operations on a thread.Thread class extends Object class and implements Runnable interface.

Commonly used Constructors of Thread class:

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

Commonly used methods of Thread class:

  1. public void start() :
    Starts the thread in a separate path of execution, then invokes the run() method on this Thread object.

  2. public void run() :
    If this Thread object was instantiated using a separate Runnable target, the run() method is invoked on that Runnable object.

  3. public final void setName(String name) :
    Changes the name of the Thread object. There is also a getName() method for retrieving the name.

  4. public final void setPriority(int priority) :
    Sets the priority of this Thread object. The possible values are between 1 and 10.

  5. public final void setDaemon(boolean on) :
    A parameter of true denotes this Thread as a daemon thread.

  6. public final void join(long millisec) :
    The current thread invokes this method on a second thread, causing the current thread to block until the second thread terminates or the specified number of milliseconds passes.

  7. public void interrupt() :
    Interrupts this thread, causing it to continue execution if it was blocked for any reason.

  8. public final boolean isAlive() :
    Returns true if the thread is alive, which is any time after the thread has been started but before it runs to completion.

The previous methods are invoked on a particular Thread object.
The following methods in the Thread class are static. Invoking one of the static methods performs the operation on the currently running thread.

  1. public static void yield() :
    Causes the currently running thread to yield to any other threads of the same priority that are waiting to be scheduled.

  2. public static void sleep(long millisec) :
    Causes the currently running thread to block for at least the specified number of milliseconds.

  3. public static boolean holdsLock(Object x) :
    Returns true if the current thread holds the lock on the given Object.

  4. public static Thread currentThread() :
    Returns a reference to the currently running thread, which is the thread that invokes this method.

  5. public static void dumpStack() :
    Prints the stack trace for the currently running thread, which is useful when debugging a multithreaded application.

Thread Class vs Runnable Interface :

  1. If we extend the Thread class, our class cannot extend any other class because Java doesn’t support multiple inheritance. But, if we implement the Runnable interface, our class can still extend other base classes.

  2. We can achieve basic functionality of a thread by extending Thread class because it provides some inbuilt methods like yield(), interrupt() etc. that are not available in Runnable interface.

Types Of Threads In Java :

There are two types of Threads in java.

  1. User Thread
  2. Daemon Thread

1. User Thread :

User threads are threads which are created by the application or user. They are high priority threads. JVM (Java Virtual Machine) will not exit until all user threads finish their execution. JVM wait for these threads to finish their task. These threads are foreground threads.

2. Daemon Thread :

Daemon threads are threads which are mostly created by the JVM. These threads always run in background. These threads are used to perform some background tasks like garbage collection and house-keeping tasks. These threads are less priority threads. JVM will not wait for these threads to finish their execution. JVM will exit as soon as all user threads finish their execution. JVM doesn’t wait for daemon threads to finish their task.

Points-To-Remember about user threads and daemon threads :

  1. We can convert user thread into daemon thread explicitly by calling setDaemon() method of the thread.
class UserThread extends Thread
{
    @Override
    public void run()
    {
        for(int i = 0; i < 1000; i++)
        {
            System.out.println("This is an user thread....");
        }
    }
}
 
public class ThreadsInJava
{
    //Main Thread
    public static void main(String[] args)
    {
        UserThread userThread = new UserThread();   //Creating the UserThread
 
        userThread.setDaemon(true);        //Changing the thread as Daemon
 
        userThread.start();               //Starting the thread
    }
}
  1. We can’t set a daemon property after starting the thread. If we try to set the daemon property when the thread is active, It will throw IllegalThreadStateException at run time.
class UserThread extends Thread
{
    @Override
    public void run()
    {
        for(int i = 0; i < 1000; i++)
        {
            System.out.println("This is an user thread....");
        }
    }
}
 
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        UserThread userThread = new UserThread();   //Creating the UserThread
 
        userThread.start();               //Starting the thread
 
        userThread.setDaemon(true);        //This statement will throw IllegalThreadStateException
    }
}
  1. We can check whether the thread is user thread or a daemon thread by using isDaemon() method of Thread class. This method returns “true” for a daemon thread and “false” for a user thread.
class UserThread extends Thread
{
    @Override
    public void run()
    {
        for(int i = 0; i < 1000; i++)
        {
            System.out.println("This is an user thread....");
        }
    }
}
 
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        UserThread userThread = new UserThread();   //Creating the UserThread
 
        System.out.println(userThread.isDaemon());    //Output : false
 
        userThread.setDaemon(true);         //changing the thread as Daemon
 
        userThread.start();                 //Starting the thread
 
        System.out.println(userThread.isDaemon());      //Output : true
    }
}
  1. Daemon property of a thread is inherited from it’s parent thread. i.e The thread created by user thread will be user thread and the thread created by daemon thread will be a daemon thread.
class Thread1 extends Thread
{
    @Override
    public void run()
    {
        Thread t = new Thread();      //Creating a child thread
 
        System.out.println(t.isDaemon());   //Checking the Daemon property of a child thread
    }
}
 
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        Thread1 t1 = new Thread1();   //Creating the Thread1
 
        t1.start();                 //Starting the thread
 
        Thread1 t2 = new Thread1();   //Creating the Thread1
 
        t2.setDaemon(true);         //changing the thread as Daemon
 
        t2.start();          //Starting the thread
    }
}
  1. The main thread or primary thread created by JVM is an user thread.

Example of User thread and daemon thread :

In the below program, The task of daemon thread will not be completed. Program terminates as soon as user thread finishes it’s task. It will not wait for daemon thread to finish it’s task.

class UserThread extends Thread
{
    @Override
    public void run()
    {
        System.out.println("This is a user thread.....");
    }
}
 
class DaemonThread extends Thread
{
    public DaemonThread()
    {
        setDaemon(true);
    }
 
    @Override
    public void run()
    {
        for(int i = 0; i < 1000; i++)
        {
            System.out.println("This is daemon thread....."+i);
        }
    }
}
 
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        DaemonThread daemon = new DaemonThread();   //Creating the DaemonThread
 
        daemon.start();                 //Starting the daemon thread
 
        UserThread userThread = new UserThread();   //Creating the UserThread
 
        userThread.start();          //Starting the user thread
    }
}

Naming A Thread :

We can give a name to a thread by using setName() method of Thread class. You can also retrieve the name of a thread using getName() method of a Thread class. These two methods are public and final. Below is the method signatures of these methods.

  1. public final void setName(String name) :
    It changes the name of the thread to “name”.
  2. public final String getName() :
    Returns the name of the thread.

Below example shows how to use setName() and getName() methods.

Example :

class MyThread extends Thread
{
    @Override
    public void run()
    {
        System.out.println("Keep some task here....");
    }
}
 
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        MyThread thread = new MyThread();     //Creating a thread
 
        thread.start();                     //Starting a thread
 
        thread.setName("My Thread");        //Giving a name to the thread
 
        String name = thread.getName();    //Retreiveing the name of the thread
 
        System.out.println(name);   //Output : My Thread
    }
}

Note :

  • setName() method may throw a SecurityException at run time if the current thread can not modify the name of the specified thread.
  • You can change the name of a thread at any state of the thread.
  • In Java, All threads have names. If we are not providing the name to a thread, thread will get default name. Default name of the thread will be consist of a word “Thread”, followed by hyphen (-) and followed by an integer number starting with 0.
  • How to retrieve a name of the primary thread or main thread ?
    First, get the reference of the main thread by using currentThread() method of Thread class. currentThread() method returns the reference of currently executing thread. After, getting the reference of the main thread, use the getName() method to retrieve the name of the thread.
public class ThreadsInJava
{
    public static void main(String[] args)
    {
        Thread t = Thread.currentThread();
 
        System.out.println(t.getName());    //Output : main
    }
}
  • Naming a thread is very useful in identifying a thread and also in debugging a code.