Introduction

Every thread in Java is created and controlled by a unique object of the java.lang.Thread class. When a standalone application is run, a user thread is automatically created to execute the main( ) method. This thread is called the main thread. In Java, we can implement threads in one of two ways:

  • By implementing the java.lang.Runnable interface
  • By extending the java.lang.Thread class

 

// Creating a thread
Thread thread = new Thread();

// Starting the thread
thread.start();

 

Extending Thread class

You can create a new thread simply by extending your class from Thread and overriding it’s run() method. The run() method contains the code that is executed inside the new thread. Once a thread is created, you can start it by calling the start() method.

// Thread example by extending 'Thread' class
public class ThreadExample extends Thread {

  // Contains the code that is executed by the thread.
  @Override
  public void run() {
    // Print the thread name
    System.out.println("Inside : " + Thread.currentThread().getName());
  }

  public static void main(String[] args) {

    // Create new thread  
    Thread thread = new ThreadExample();

    // Start the thread
    thread.start();

    // Setting the name of thread1 
    thread.setName("child thread xyz"); 
    
    // Setting the priority of thread 
    thread1.setPriority(5); 

    // Fetching an instance of this thread i.e. reference to the thread that is currently executing.
    Thread t = Thread.currentThread(); 

    // Thread name
    System.out.println(t.getName()); 

    System.out.println("Thread1 ID: " + thread.getId()); 
              
    // Priority and state of thread
    System.out.println("Priority of thread = " + thread.getPriority()); 
    System.out.println(thread.getState()); 
  }
}

 

Implementing Runnable

Runnable interface is the primary template for any object that is intended to be executed by a thread. It defines a single method run(), which is meant to contain the code that is executed by the thread. Any class whose instance needs to be executed by a thread should implement the Runnable interface. The Thread class itself implements Runnable with an empty implementation of run() method. For creating a new thread, create an instance of the class that implements Runnable interface and then pass that instance to Thread (Runnable target) constructor.

 

// Thread by implementing Runnable interface 
class RunnableExample implements Runnable{
 
  public static int myCount = 0;

  public RunnableExample(){
       
  }

  // Function called when thread starts
  public void run() {

    while(RunnableExample.myCount <= 10){
    
      try{
        System.out.println("Thread: "+(++RunnableExample.myCount));
        Thread.sleep(100);
      } catch (InterruptedException iex) {
        System.out.println("Exception in thread: "+iex.getMessage());
      }
    }
  } 
}

public class Main {

  public static void main(String a[]){
 
    RunnableExample mrt = new RunnableExample();
 
    // Create thread
    Thread t = new Thread(mrt);
    
    // Start the thread
    t.start();

    // Create thread with name "New Thread"
    Thread thread = new Thread(mrt, "New Thread");
  }
}

 

 

// Thread example by implementing Runnable interface and Anonymous class
public class RunnableExampleAnonymousClass {
  public static void main(String[] args) {

    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
      }
    };

    Thread thread = new Thread(runnable);

    thread.start();
  }
}

 

Pause a Thread

A thread can pause itself by calling the static method Thread.sleep() . The sleep() takes a number of milliseconds as parameter. The sleep() method will attempt to sleep that number of milliseconds before resuming execution. The Thread sleep() is not 100% precise, but it is pretty good still.

 

Stop a Thread

Stopping a thread requires some preparation of your thread implementation code. Threadclass in Java contains a stop() method, but it is deprecated. The original stop() method would not provide any guarantees about in what state the thread was stopped. That means, that all Java objects the thread had access to during execution would be left in an unknown state. If other threads in your application also has access to the same objects, your application could fail unexpectedly and unpredictably.

Instead of calling the stop() method you will have to implement your thread code so it can be stopped. Here is an example of a class that implements Runnable which contains an extra method called doStop() which signals to the Runnable to stop. The Runnable will check this signal and stop when it is ready to do so.

public class RunnableStop implements Runnable {

  private boolean stopThread = false;

  public synchronized void stop() {
    this.stopThread = true;
  }

  private synchronized boolean keepRunning() {
    return this.stopThread == false;
  }

  @Override
  public void run() {
    while(keepRunning()) {
    
      // keep doing what this thread should do.
      System.out.println("Running");

      try {
        Thread.sleep(3L * 1000L);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

 

The stop() method is intended to be called from another thread than the thread executing the RunnableStop's run() method. The keepRunning() method is called internally by the thread executing the MyRunnable’s run() method. As long as stop() has not been called the keepRunning() method will return true i.e. the thread executing the run() method will keep running.

 

join() Method

The join() method allows one thread to wait for the completion of the other. In the following example, Thread 2 waits for the completion of Thread 1 for 1000 milliseconds by calling Thread.join(1000), and then starts the execution –

public class ThreadJoinExample {

  public static void main(String[] args) {
    // Create Thread 1
    Thread thread1 = new Thread(() -> {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }});

    // Create Thread 2
    Thread thread2 = new Thread(() -> {
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    }});

    // Start the thread1
    thread1.start();

    // Waiting for Thread 1 for 1000 milliseconds or it's dead 
    try {
      thread1.join(1000);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }

    // Start the thread2
    thread2.start();
  }
}

 

Runnable vs Thread

  • Implementing Runnable is preferred because java supports implementing multiple interfaces. If you extend Thread class, you can’t extend any other classes.
  • Creating an implementation of Runnable and passing it to the Thread class utilizes composition and not inheritance – which is more flexible.
  • When we extend Thread class, each of our thread creates unique object and associate with it. When we implements Runnable, it shares the same object to multiple threads.