Threads in Java

Lakshini Kuganandamurthy
SLIIT Women In FOSS Community
8 min readMay 10, 2022

This was a very difficult topic for me to learn during my university days maybe because I thought it was hard and boring!!! Until recently, I found it more interesting. So, here you go, some knowledge I gained recently!!!

Believe me, it is gonna be easy!!! Let’s dive into the basics!!!

Intro image

It has always been a misunderstanding, that threads are there to cut down time!!! For e.g. if there is a process running for 10 mins, there is no guarantee that you can use 10 threads to cut down the time to 1 min (to make the process run only for a minute).

Let’s prove this by an example. This is not a real-world example and the process steps and durations are just assumed!!!

Say we have a 5 step process as follows :

  1. Validate takes 1s to complete.
  2. Preparation takes 2s to complete.
  3. Calculating the currency rate takes 5s to complete.
  4. Updating the model takes 2s.
  5. Saving to the database takes 1s.

Therefore the total duration the process will run is 11s. Ok, since there are 5 steps, do you think it is possible to use 5 threads to cut down the time to, say (11s / 5 = 2s)??? Not at all, it is not possible to use 5 threads to cut down time from 11s to 2s. But why???

This is because there are dependency steps or tasks in the process. It is impossible to save data before validating and preparing it, so the step, “saving to database” depends on both the tasks, “validating and preparing the data”. Therefore, it is not practical to have a thread to perform the saving task since it depends on other tasks (because before completing those tasks, saving cannot be done). But, using threads we can perform certain tasks parallelly to cut down time to 7s or 8s from the original 11s. Such as we can have two threads running concurrently, one thread to perform preparation and validation tasks and another to perform calculating the currency rate, parallelly.

The point is, We cannot use separate threads for each task and cut down processing time, if these tasks are dependent. There is no way that you increase the number of threads and cut down processing time drastically!!! This is of course not Multithreading!!!

Then what does multithreading mean??? Multithreading means multitasking!!! There are two types of multitasking namely,

  1. Process-based multitasking — We, software engineers, do enjoy listening to music while developing a cool app right!!! Maybe there is also a movie being downloaded at the same time or we want a break and we browse Facebook. Here, there are multiple processes running at the same time on our computer. In a given time, there are 3 or 4 processes running. If the movie download is paused for some time, will that stop the music we listen??? or terminate our browsing window??? Not at all, these tasks are all independent of each other. We can kill any task at any given time, which will not affect other tasks running parallelly. This is possible because the operating system runs multiple processes at the same time, which are also known as multiple programs.
  2. Thread-based multitasking — Here, all the threads running belong to the same process or we can say the same main process running In the operating system (OS). In process-based, we saw that there were 3 different processes running in the OS. Therefore, here, one process is broken down into multiple tasks.

Let’s take the following e.g. there are 50 files to process and the tasks are to read the file and save the file to the database. This process takes 50 mins to complete. Let’s assume that the database supports multithreading (multiple threads running concurrently) i.e. two or more threads can insert the read file data to the database at the same time. When using two threads, we can cut down the time to probably 30 mins. How this really works is, that one thread takes a file, processes it, and saves it to the database. At the same time, another thread takes another file, processes it, and saves it to the database. This is possible because reading and saving each file is an independent task. No dependency among tasks, so therefore at most, we can use 50 threads to process the 50 files and cut down the time to 10 mins probably. But, there is no way that we can use 100 threads to cut down the time to 1s or 2. There is no point in using two threads for each file, maybe one thread to read the file and another to save it to the database, these cannot run parallelly because to save a file, it has to be read first!!! So, the saving task depends on the reading task!!! or simply it is a waste of resources for 2 threads to process the same file!!!

Therefore the point is, that we cannot always increase the number of threads to cut down the time drastically from mins to seconds.

Multithreading is possible in situations of independent tasks in a process.

Ok, we discussed so much about the threads, but did we say what actually a thread is???

A thread is a flow of execution and simply multithreading is defined as multiple flows of execution.

A single thread executes all the instructions in the flow of the program. If multithreading, at a certain point in the instruction flow, it may divide into multiple flows of execution as shown below.

demonstrating multithreading — multiple flows of execution

Hope, you all got an understanding!!! Now let’s see, how we can create a thread in Java!!!

Threads can be created in two ways in Java as follows :

  1. Extends Thread class
  2. Implements Runnable interface

No matter, how threads are created, the behaviour of threads will not change. But they have their pros and cons. Let’s take the e.g. shown below.

Demonstration for difference between Thread class and Runnable interface

The above diagram shows a simple inheritance, where Runner IS A Sportsman and Sportsman IS A Man and Man IS A Human. Therefore, a Runner IS A Human. The Runner is more specific than the Sportsman and the Sportsman is more specific than the Man and the Man is more specific than the Human. This is a simple explanation of inheritance!!!

These are all considered Java classes by the way!!!

Now, if we want to convert the Runner to a Thread, we create another inheritance relationship between the Runner and Thread classes as shown above. Then Runner extends Thread. This extension would break the relationship between Runner and Sportsman. Runner is no longer a Sportsman but IS A Thread. This implies that Runner is no longer a Human too!!! The multi-level inheritance relationship is broken, the moment Runner extends Thread!!! So, this is one disadvantage when creating threads by extending the Thread class.

This is because Java does not support multiple inheritance, i.e. Java classes can only extend or inherit from one parent class at a time. There can’t be classes inheriting multiple parent classes at the same time!!! Runner either has to inherit Sportsman OR Thread!!!

Creating a thread by implementing the Runnable interface has no such disadvantages as breaking relationships because Java supports multiple implementations. So, Runner can always implement the Runnable interface and other interfaces at the same time.

Now, let’s discuss the actual Thread code, with Thread class methods!!!

Here, we have a class Printer that extends Thread class. Therefore, we will override the run() method of the Thread class, to print from 0–1000.

Following class has the main method, in which the Printer class is instantiated. We instantiate a Thread i.e., we create a thread. Then we call start() method, but there is no start() method in the Printer class. Printer inherits from Thread class, so start() method of the Thread class is called here.

If you remembered, JVM, when created or started, creates a non-daemon thread that runs the main method. Later, this main thread also prints from 0–100, as shown below in code. Upto line 5, there is only one thread running, which is the main thread. When you start the new Thread at line 10, there will be 2 threads running in the program. The newly created thread is a child thread of the main thread. But there is no guarantee that the moment we call the start() method on the newly created thread, it will start instantly. This is because the thread has to go through several steps for it to officially start running.

After start() method is called on the new Thread, (line 10 is just an instruction to the main thread similar to other instructions in the main method), it executes the remaining instructions of printing from 0–100.

Ok, since you have understood the above code, let’s discuss a tricky interview question based on this scenario.

As you saw, the main thread starts a new thread which is the child thread of the main thread and it is supposed to print from 0–1000. After calling the start() method on its child thread, main thread continues to execute line 13 and prints from 0–100. During this time, the child thread would have started running and printing. Logically, 0–100 is printed fairly quicker than 0–1000.

So the interview question is will the program or (more clearly the JVM) terminate after the main thread completes printing from 0–100 (say during this time, the Printer thread is printing somewhere around 200), this way child thread won’t be able to complete its job, or will the program wait till the child thread completes and then exit???

Most developers think that JVM terminates or exits (i.e. the running application terminates) when the main thread exits. But this is totally wrong!!!

JVM terminates or exits only in either of the following 2 ways :

  1. When programmer explicitly invokes exit() method -> System.exit()
  2. When the last non-daemon thread exits.

Here, we did not mention explicitly whether the new thread is a daemon or non-daemon thread, it will have the default behaviour.

The default behaviour is that we already know that JVM, when started creates a non-daemon thread that runs the main method and is thus called the main thread. Therefore any threads created in the main method become the child thread of the main thread and inherit its daemon status. So, the new thread created at line 6 is also a non-daemon thread. So, the obvious answer is that the JVM will wait till the new thread completes its job, printing from 0–1000, and then exit. Even though the main thread exits after printing 0–100, there is still a non-daemon thread running, so, JVM will exit once that thread completes 0–1000, and exits.

Phew!!! Hope you understood the basics we discussed so far. Let’s wrap up for now!!!

Happy Learning!!!

--

--

Lakshini Kuganandamurthy
SLIIT Women In FOSS Community

A passionate individual eager to learn and improve. Associate Software Engineer, Virtusa.