Handling Threads in Java

Threads are lightweight sub-processes that belong to a certain process. These are categorized under thread-based multitasking.

Mainly multitasking is divided into two categories.

  • Process-based: OS runs multiple programs at the same time but does not do any effect on each other. In here resources are not shared between each other.
  • Thread-based: Threads are belonging to a certain process and share the same resources at run time.

Creation of Threads

Mainly there is 2 type of threads in java called main and child threads. The main thread is created automatically when a program runs and this is responsible for creating new child threads. Refer to figure 1.

Figure 1. Thread Hierarchy.

We can refer to the main thread by calling the method currentThread(). The default priority of the Main thread is 5 and other child threads created by this are also inherit the default priority as 5.

We can create threads in two ways.

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

The following describes the creation of threads in detail.

  • Extending the Thread Class

When you create a thread by extending the Thread class, it’s not a must to declare (override) start and run methods inside your extended class. Because when you call the start method inside your Main class, this will call the method declared inside the extended class. But if you haven’t declared such a method inside the extended class, then due to the OOP implementation, call the start method of the superclass. Inside this start method, calls the run method in the extended class, and same as previously, if there is no run method in the subclass then it calls for the run method declared in the superclass.

The main disadvantage of Thread class extending is loss of inheritance. That’s mean if your class has inherited from another class and you are trying to extend the class with Thread class you will lose the inheritance because Java cannot handle multiple inheritances.

  • Implementing the Runnable Interface

When we use the Runnable interface instead of extending the Thread class, we must override the run method inside the class otherwise the class should declare as an abstract class.

Constructors of Thread Class

Mainly there are 8 constructors in the Thread class.

  • Thread(): Creates a new object of Thread class with no arguments.
  • Thread(Runnable target): Creates a new object of Thread class using an object of the class which implements the Runnable interface.
  • Thread(String name): Creates a new object of Thread class with the name of the thread.
  • Thread(Runnable target, String name): Creates a new object of Thread class using an object of the class which implements the Runnable interface and the name of the thread.
  • Thread(ThreadGroup group, Runnable target): Creates a new object of Thread class with an object of ThreadGroup class and an object of the class which implements the Runnable interface.
  • Thread(ThreadGroup group, String name): Creates a new object of Thread class with an object of ThreadGroup class and the name of the thread.
  • Thread(ThreadGroup group, Runnable target, String name): Creates a new object of Thread class with an instance of ThreadGroup class, an object of the class which implements the Runnable interface and the name of the thread.
  • Thread(ThreadGroup group, Runnable target, String name, long stackSize): Creates a new object of Thread class with an instance of ThreadGroup class, an object of the class which implements the Runnable interface, name of the thread, and size of the stack located in heap.

Thread Life Cycle

As in Figure 2. the thread has 5 states.

Figure 2. States of a Thread.
  • New: When the thread is created
  • Ready: When executing the start method
  • Running: When executing the run method
  • Wait: Wait until others to complete their job or give a chance to others to execute. Threads change their state from wait to ready when,
  1. A timeout

2. An interrupt

3. Other thread/s has completed their job

  • Dead: When the run method has completed the execution

Methods in Thread Class

Here define some of the methods declared in Thread class.

  • start()

Before calling the run method, this completes some tasks such,

  1. Check whether the current child thread is already running

2. Check whether the current thread is ready to run

3. Add to the thread pool

4. Register the thread on registers, etc.

After completing the above tasks, this calls for the run method.

  • run()

Defines the things that need to be run within the thread.

  • join()

Waits until others to complete their job. This method allows overloading.

  1. join()

2. join(long milliseconds)

3. join(long milliseconds, int nanoseconds)

  • yield()

This is a static method declared in Thread class. The yield method provides chances to execute other waiting threads of the same priority while the method implemented thread is in the waiting state.

  • sleep()

This is used to sleep a thread for a specified amount of time. Thread class has 2 methods for sleep (overloading).

  1. sleep(long milliseconds)

2. sleep(long milliseconds, int nanoseconds)

  • setPriority() and getPriority()

This method allows the programmer to set priorities for the threads. When it creates child threads from the main thread, child threads are inheriting the priority value which is defined for the main thread. The priority values should be integers and must lie within 1 and 10 otherwise this will lead to giving an IllegalArgumentException at runtime. When setting the priority, the highest priority is set to 10 and the lowest priority is set to 1. The default priority of a thread is 5. When there is more than one thread with the same priority, the scheduler will choose a thread to execute arbitrarily.

  • interrupt()

If the thread is in sleeping or waiting state this method can interrupt the thread execution by throwing InterruptedException. But a single interrupt works only for a single sleep.

The following example shows, how these methods are implemented.

public class ThreadExample extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++){
try {
sleep(1000);
System.out.println("child thread "+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MainThread {
public static void main(String[] args) throws InterruptedException {
ThreadExample threadExample = new ThreadExample();
threadExample.setDaemon(true);
threadExample.start();
threadExample.interrupt();
threadExample.join(2500);
for (int i = 0; i < 5; i++){
System.out.println("Main thread "+i+ " "+ Thread.currentThread().getName());
}
System.out.println("*******************************************");

}
}

Here, I have created the ThreadExample by extending the Thread class. Inside the ThreadExample class, I have overridden the run method. In MainThread class I have created an object of ThreadExample class, set the child thread as a daemon thread, and called the interrupt method.

java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at ThreadExample.run(ThreadExample.java:6)
child thread 1
child thread 2
Main thread 0 main
Main thread 1 main
Main thread 2 main
Main thread 3 main
Main thread 4 main
*******************************************
Process finished with exit code 0

Here clarifies how I obtained the above output.

When the child thread starts executing, the child thread is set as a daemon thread. Here I have put the main thread in the waiting state and because of that main thread allows executing the child thread within 2500ms. As in the run method child thread waits for 1000ms but at the same time interrupt method throws the InterruptedException. Due to a single interrupt works only for a single sleep, executes the commands of the child thread up to 2 times (waiting time is 2500ms) without any interruption and again handover the execution to the main thread. Then it completes the execution of the main thread but this does not complete the execution of the child thread because I have set the child thread as a daemon thread and daemon threads cannot exist without any non-daemon threads.

Some Facts about Java Threads

  • When start executing a thread, there is no guaranty about the order of execution and this depends on the thread scheduler of the specific JVM.
  • Daemon threads cannot exist without non-daemon threads. Because of that, it’s better to make the thread that has much work as a non-daemon.
  • Java programs get terminated when,
  1. Calling System.exit(0): Destroys the current instance of JVM.

2. The last non-daemon thread is get destroyed.

References

Final year undergraduate in Computer Science, Associate Software Engineer.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store