THREADS IN JAVA

Hansini Rupasinghe
Nerd For Tech
Published in
8 min readMay 10, 2021

--

● A Thread is a flow of execution.

● A thread executes multiple instructions at the same time.

● Threads are utilized to carry out complex tasks in the background without interrupting the main program.

Multi-threading

Multithreading can be referred to as multiple flows of executions that are processing simultaneously. This is also know as ‘multitasking’.

✦ There is a possibility that at one point, all the threads can join and go as one thread from that point onwards.

Facts to remember:

🟠 There is no way that you can reduce the time significantly by just increasing the number of threads.

🟠 In thread-based multitasking, those threads belong to one process itself.

Thread Life Cycle

  1. New

A thread comes to new state at the moment you create an instance of Thread class. When a thread is in new state, it has not yet started to execute.

2. Ready / Runnable

The thread is ready to execute once it is moved to runnable state.

3. Running

If thread scheduler has selected and assigned a task, it is in running state. A thread can move back to ready state in certain instances.

4. Dead

When the thread’s run() method exits, it is in dead state. Once a thread is dead, it cannot move back to any previous stages.

🔴 Between these steps, there are other multiple steps.

ex: Threads can go to block state (Temporary hold states)

⚫️ RULE: If any thread goes to waiting state, there is no way direct way to go to the running state.

In order to go to ready state from waiting;

  1. When thread 2 completes its task
  2. When time outs
  3. When interrupted

Let us take an example.

Question:

Why cannot we use 5 threads and cut off the time into 11/5 s ❓

💠 Because they are dependency tasks.

💠 You can do tasks parallelly. For that, you can use 2 different threads and cut down time a little less than 11s. But there’s no way that you can increase the number of threads and cut down the process up to 1s or 2s.

How can we create threads in Java ❓

There are 2 ways.

  1. Extend Thread class
  2. implement Runnable Interface

🟣 No matter how you create a thread, the behavior of a thread and what it does will not change.

Extend Thread Class

Let us observe the following diagram.

If you convert Runner into a thread (i.e. extending Thread class), it breaks this relationship. When extending the Thread class, Runner ➝ Sportsman relationship will break. This happens because Java does not support multiple inheritance.

🔴 There is nothing called “Extending Thread class is would be better than implementing Runnable Interface”.

SCENARIOS RELATED TO THREADS

Scenario 1: Is it valid to override run() method when you extend thread class?

When you invoke run() method, it goes and check if there is any run() method in Printer class.

When you go to printer.start() method in Application.java, it checks in the Printer.java if there is any start() method in Printer class. As there is nothing, it goes to parent class (Thread class)

In Thread class;

🔹 There is a start() method.

🔹 Within start() method, run() is invoked.

OUTPUT:

It will be executed, but nothing will happen.

If you implement a Runnable Interface, it forces to override run() method since it is the behavior of an instance. If you implement any interface, you must override all the methods within interface.

Scenario 2: Can we stay without overriding run() method when you implement multiple threads?

Yes, but there is a condition.

It must come from ‘extends Thread’ class (Not implementation)

If you do not override run() method, there won’t be any job for that thread. Therefore, nothing will happen.

Scenario 3: Why do we have different orders in each executions when executing the below program many times?

When executing, we cannot predict which thread (whether it is the main thread or the child thread) will execute first since there is no rule for the execution order. When a thread starts to execute, it will add your thread to Thread Scheduler which is entirely depending on your JRE (JVM). Thread scheduler decides which thread to go. And this order changes from JVM to JVM.

Scenario 4: What happens when you invoke run() method instead of start() method?

OUTPUT:

It will always execute child class before executing main.

We only have one thread. (Not 2 threads)

Q: Why does it force to call start() method?

🟠 To make your life easy.

🟠 When you call start() method, JVM does all the above mentioned tasks, necessary things and invoke run() method.

Scenario 5: What happens when you override a start() method on your Thread class?

printer.start() method gives the chance to immediate class. The start() method in the Printer class gets executed when we invoke the start method in main class.

When overriding the start method, you have blocked an opportunity to create a new thread because you are blocking Thread class to invoke start() method. Therefore, it will not create a new thread, will not add to register, will not do anything, so it will not work.

Scenario 6: Can you overload main method?

Yes, but JVM always considers public static void main (String[] args) signature.

Scenario 7: Can you override start() method and allow creating a thread?

Then it has created a new thread. When you call super.start(), it goes to parent class which is the Thread class’s start() method and it will create a thread and add to the pool. This time, that thread immediately starts its execution.

Scenario 8: What happens if you overload run() method?

Nothing will happen and it will create a thread. But Thread class will always invoke run() method with no arguments.

Scenario 9: What happens if your main thread does more job than your child thread?

If main thread leaves main method, it will NOT terminate java program. Child thread can continue its work even though main thread is done with its work.

Child thread must be set as a Daemon thread if you want to terminate child thread when main thread terminates.

(.setDaemon(true))

A program will always terminate only when the last non-daemon thread terminates.

Implementing Runnable Interface

● Runnable is an interface which does not contain any implemented methods.

When extending a Thread class, your class extends Thread class and Thread implements Runnable. We have an intermediate class called Thread. It implements all useful methods from Runnable Interface.

When you create an object from Thread class, you can pass the Runnable instance for that.

In the below code snippet, we create a Thread class instance and pass our instance from the Runnable interface. That means, we pass printer instance.

Thread Priority

🔸 Thread priority is not an index, it is a value.

🔸 Thread priorities ranges from 1 to 10.

Highest Priority ➞ 10

Lowest Priority ➞ 1

🔸 In any application, default priority of the main thread is 5. Thereafter, any thread you create will inherit parent thread’s value.

🔸 There is NO SUCH RULE that, always the thread priority is 5.

Example:

If main thread creates t1, t1 inherits main thread’s priority. Then you can change the priority of t1.

Consider that the thread priority of main thread is 5. And then you create t1, so now the priority of t1 becomes 5. Then you set t1’s priority to 7. And if you create t2 with t1 thread, t2 will inherit t1 thread’s priority.

What happens if you set a priority beyond 1-10?

It will throw an IllegalArgument Exception because you ae setting something beyond the limit of thread priority.

What happens if two threads carry the same priority?

Nothing significant will happen. The thread scheduler will choose a thread to run and you cannot predict.

Thread Join Method

It allows currently running threads to stop executing until another thread completes its task.

There are 3 different overloaded join functions.

  1. join()
  2. join(long millis)
  3. join(long millis, int nanos)

✵ If time has not mentioned, it waits forever. If the other thread completes its work before given time, then it can resume.

Thread Yield Method

● Yield() method can be used if you want to use debugging or change/give more chance to a particular thread.

● You do not get the use of multithreading when you use yield().

● If you have 2 different threads, any thread can call yield(). The moment yield() is called, thread gives a signal to Thread Scheduler to give chance to others.

● Yield() is a native method. (Not implemented in Java)

✽ T1 calls yield() method.

✽ Either T2 or T3 will get the chance.

✽ If T3 got the chance, it cannot be assured that T1 would again get a chance when t3 finishes his work on the particular time slot. It will probably be T2. (This is because T1 is again a usual waiting thread on thread scheduler)

Thread Sleep Method

● It can wait for a certain given time.

● There are 2 different method signatures (overloading)

  1. public static void sleep(long miliseconds)throws InterruptedException ➞ Native Method

2. public static void sleep(long miliseconds, int nanos)throws InterruptedException ➞ Not a Native Method

● In this sleep() method, thread will go to the waiting state for a certain given time and it comes back if;

🟢 Waiting time is expired

🟢 If any interrupt occurs

Thread Interrupt Method

● The interrupt() method of thread class is used to interrupt the thread.

● The moment you execute interrupt() method, whatever the thread in waiting state will come back.

References

--

--