Multi Threading In Java

Content

  • Introduction

  • The ways to define a thread

  • Getting and Setting Name of Thread

  • Thread Priority

  • The methods to prevent Thread executions

    1. yield() 2.join() 3.sleep()

  • Synchronization

  • Inter Thread Communication

  • DeadLock

  • Deamon Thread

  • MultiThreading Enhancement

Introduction

Multitasking

Executing multiple task simultaneously is the concept of Multitasking.

The main advantage of Multitasking is to increase performance by reducing response time of the system.

Example :- Class room student(Listening,Writting,Checking mobile phones,Sleeping,Observing environment)

Multitasking is two types

  1. Process Based Multitasking

  2. Thread Based Multitasking

Process Based Multitasking

Executing several tasks simultaneously where each task is a separate independent program(process), such type of multitasking is called Process Based Multitasking.

Example :-A programmer can do below tasks simultaneously and these are independent of each other.

  1. Typing a java program in editor.

  2. Listening Music from same system.

  3. Downloading file from internet.

Process Based Multitasking is best suitable at OS level.

Thread Based Multitasking

Executing several tasks simultaneously where each task is a separate independent part of same program , such type of multitasking is called Thread Based Multitasking. And each independent part is called Thread.

Thread Based Multitasking is best suitable at programmatic level.

The main application area of Multithreading are

  1. To develop multimedia graphics.

  2. To develop Animation's.

  3. To develops Video games.

  4. To develop web servers and application servers.(Tomcat uses 60 threads by default).

When compare with old language, developing multithreaded application in java is very easy because java provides inbuild support for Multithreading with rich Api[Thread,Runnable,ThreadGroup,...].

Real life Example :-Requirement is to find some key word in my entire file system, when the contents of file matches this keyword ,print the file name. In general first we will search in C drive, then D drive ,then E drive which will take a lot of time.

If we implement thread in each folder of each drive by creating 60+ threads, with in some sec we will complete the task.

A flow of execution is called a Thread. For every thread is a separate independent Job .

JVM runs main thread when ever we run the application. If we create our new thread then main thread will create a child thread and child thread will call their run method.

The ways to define a thread

There are two ways to define a Thread.

  1. By extending Thread Class

  2. By implementing Runnable Interface

By extending Thread Class

class MyThread extends Thread{
    @Override                                                     //Override the run()
    public void run() {
         for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread");
            }
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();                                     //starting of  child Thread(myThread)
            for (int i = 0; i < 5; i++) {
                System.out.println("Main Thread");
            }                                
    }
}

Thread Scheduler

It is the part of JVM.

  • It is the responsible to schedule threads i.e. if multiple threads are waiting to get a chance of execution then in which order threads will be executed is decided by thread scheduler.

  • We cant except exact algorithm by thread scheduler ,it is very from JVM to JVM. Hence we cant except thread execution order and exact output.

  • Hence whenever situation coms to multi threading, there is not guaranty for exact output but we can provide several possible outputs.

Following are various possible output for the above programs.

Possible-1 :-Main Thread(5 times) then Child Thread(5 times)

Possible-2:-Child Thread(5 times) then Main Thread(5 times)

Possible-3:-Main Thread,Child Thread,Main Thread,.............

Difference betwwen thread.start() vs thread.run()

class MyThread extends Thread{
    @Override                                                     //Override the run()
    public void run() {
         for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread");
            }
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.run();                                     //called by main thread
            for (int i = 0; i < 5; i++) {
                System.out.println("Main Thread");
            }                                
    }
}
  • In case of myThread.start(),a new thread will be created which is responsible for the execution of run() method.

  • In case of myThread.run(), a new thread won't be created and run method will be executed just like a normal method call by main thread .

  • Hence the output of the above program is - Child Thread(10 times) followed by Main Thread(10 times)

  • This total output produced by only main thread.

Importance of Thread.start() mthod

Thread class start method is responsible to register with Thread Scheduler and all other mandatory activities. Hence without executing thread class start method there is no chance of starting new thread in java. Due to this thread class start method is consider as heart of multi threading.

Inside the Thread.start() method

start(){
- Register the thread with Thread Scheduler
- Perform all other mandatory activities.
- Invoke run().
}

Overloading of run() method Overloading of run() method is always possible but thread class start method can invoke no argument run() method. The other over loaded method we have to call explicitly like a normal method call.

class MyThread extends Thread{
    @Override                                                     //Override the run()
    public void run() {
         for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread no args run method");
            }
    }
    public void run(int i) {
         for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread with overloading");
            }
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();                                 

    }
}

The output-Child Thread no args run method

If we are not Override run() method

If we are not Override run() method then thread class run() method will be executed with has empty implementation. Hence we won't get any output.

class MyThread extends Thread{
    //No run()
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();                                 

    }
}

The output-No Output

It is highly recommended to override run() method otherwise don't go for multi threading concept.

Override start() method

If we override start() method, then our start() method will be executed just like a normal method call and the new thread won't be created. All operation will be done by main thread.

class MyThread extends Thread{

    public void run() {
                System.out.println("Run method");
    }

    public void start() {
                System.out.println("Start method");
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();        
        System.out.println("Main Method");                 
    }
}

The output-Start method Main Method

It is not recommended to override start() method otherwise don't go for multi threading concept.

**What if we call super.start() method in our override start() method **

After super.start(),new child thread will be created and only responsible to for run() method. Main thread will run remaining code.

Main thread will print -'Start method'-'Main Method'.

Child thread will print only 'Run method'.

class MyThread extends Thread{
      public void start() {
                 super.start();
                System.out.println("Start method");
    }                                  
    public void run() {      
                System.out.println("Run method");
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();        
        System.out.println("Main Method");                 
    }
}

Possible output:-

  • 'Run method'-'Start Method'-'Main Method'

  • 'Start method'-'Run Method'-'Main Method'

  • 'Start method'-'Main Method'-'Run Method'

  • 'Main method'-'Start Method'-'Run Method'(This output is not possible because main thread execute sequentially)

Thread Life Cycle

MyThread myThread=new MyThread () -->New/Born state

myThread.start() -->Ready/Runnable state

After Thread Scheduler allocate processor -->Running state

if run() completed -->Dead state

If we restart the same thread

After starting a thread, if we are trying to restart the same thread, we will get run time exception saying IllegalThreadStateException .

class MyThread extends Thread{                             
    public void run() {      
                System.out.println("Run method");
    }
}
public class CreateThreadByClass {
    public static void main(String[] args)
    {
        MyThread myThread=new MyThread ();   //Thread Initiation
        myThread.start();        
        System.out.println("Main Method");      
        myThread.start();                   
    }
}

The output-Main Method-Run method-java.lang.IllegalThreadStateException

By implementing Runnable Interface

We can define a thread by implementing Runnable Interface .

In by extending Thread Class :- Mythread extend Thread class and Thread class internally implementing Runnable interface.

Here we will direct implement the Runnable Interface .

Runnable Interface present in java.lang package and it contains only one method-run() method.

class MyRunnable implements Runnable{
    public void run(){
         for (int i = 0; i < 5; i++) {
            System.out.println("Child Thread");      //executing by child thread
           }
    }
}
public class CreateThreadByInterface {
    public static void main(String[] args){
        MyRunnable myRunnable =new MyRunnable();
       Thread thread1=new Thread(myRunnable);
        thread1.start();
         for (int i = 0; i < 5; i++) {
            System.out.println("Main Thread");     //will execute by main thread
           }
      }
}

We will get mixed output and we can't tell exact output.

Loophole in implementation

public class CreateThreadByInterface {
    public static void main(String[] args){
        MyRunnable myRunnable =new MyRunnable();
       Thread thread1=new Thread();                      //with out runnable class
       Thread thread2=new Thread(myRunnable);
        thread1.start();         //case-1
       thread1.run();              //case-2
       thread2.start();            //case-3
       thread2.run();              //case-4
       myRunnable.start()     //case-5
       myRunnable.run();       //case-6
      }
}

From above example

  • Case-1:- A new thread will be created and which is responsible for the execution of thread class run method, which has empty implementation.

  • Case-2:- No new thread will be created and thread class run method will be executed just like a normal method call.

  • Case-3:- A new thread will be created and which is responsible for the execution of MyRunnable class run method.

  • Case-4:- A new thread won't be created and MyRunnable class run method will be executed just like a normal method call.

  • Case-5:- We will get compile time error saying MyRunnable class don't have start method. Cant find symbol method start() in MyRunnable class location.

  • Case-6:-No new thread will be created and MyRunnable class run method will be executed just like a normal method call.

**Which approach is recommended to create thread **

  • Java does not support multiple inheritance. If we extend Thread class ,we can't extend any other class further.

  • But we can implement multiple interface. So by implementing Runnable Interface is recommended.

Thread class constructors

Thread t=new Thread();
Thread t=new Thread(Runnable r);
Thread t=new Thread(String name);
Thread t=new Thread(Runnable r,String name);
Thread t=new Thread(ThreadGroup g,String name);
Thread t=new Thread(ThreadGroup g,Runnable r);
Thread t=new Thread(ThreadGroup g,Runnable r,String name);
Thread t=new Thread(ThreadGroup g,Runnable r,String name,long stackSize);

A new way to define a thread(Not Recommended)

class MyThread extends Thread{
    @Override                                                     //Override the run()
    public void run() {
         for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread");
            }
    }
}
public class CreateThreadByInterface {
    public static void main(String[] args){
        MyThread myThread =new MyThread();
       Thread thread1=new Thread(myThread);
        thread1.start();
         for (int i = 0; i < 5; i++) {
            System.out.println("Main Thread");     //will execute by main thread
           }
      }
}

Define a thread using Java 8

public class DemoThread{
    public static void main(String[] args){

        Thread myThread =new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("Child Thread");
            }
        });
        myThread .start();
         for (int i = 0; i < 5; i++) {
            System.out.println("Main Thread");     //will execute by main thread
           }
      }
}

Name of Thread

Every thread in java has some name, it may be default name generated by JVM or customized name provided by programmer.

We can get and set name of a thread by using the following two methos of thread class.

public final String getName()
public final void setName(String name)

Example

class MyThread extends Thread{

}
public class DemoThread{
    public static void main(String[] args){
        System.out.println(Thread.currentThread().getName());   //main(default name)
        MyThread myThread =new MyThread();
        System.out.println(myThread.getName());    //Thread-0(default name)
        Thread.currentThread().setName("New Name");
        System.out.println(Thread.currentThread().getName());   //New Name
      }
}
  • We can get current executing thread object by using-Thread.currentThread();
class MyThread extends Thread{                                         
    public void run() {
     System.out.println(Thread.currentThread().getName());  //Thread-0
    }
}
public class DemoThread{
    public static void main(String[] args){
        MyThread myThread =new MyThread();
        myThread .start();
       System.out.println(Thread.currentThread().getName());  //main
      }
}

Thread Priorities(1-10)

Every thread in java has some priority, it may be default priority generated by JVM or customized priority provided by programmer. The valid range of thread priority is 1-10 where 1 is min priority and 10 is max priority.

Thread class defines the following constants to represent some standard priorities.

  • 1 -> least priority(Thread.MIN_PRIORITY);

  • 10 -> high priority(Thread.MAX_PRIORITY);

  • 5 -> normal priority(Thread.NORM_PRIORITY)(default value)

Thread scheduler will use priorities while allocating processor. The thread which is having highest priority will get chance first.

If two threads having same priority ,then we can't except exact execution order. It dependents on thread scheduler.

Thread class defines the following methods to get and set priority of a thread.

public final int getPriority(){}
public final void setPriority(int i){}

Allowed values range 1-10,otherwise run time exception-IllegalArgumentException.

Example->myThread .setPriority(17) ->IllegalArgumentException.

The default priority only for main thread is 5.But for all remaining threads default priority will be inherited from parent to child i.e whatever priority parent thread have, the same priority will be there for the child thread.

class MyThread extends Thread{                                         
    public void run() {
     System.out.println(Thread.currentThread().getName()); //child thread executes this line
    }
}
public class DemoThread {
    public static void main(String[] args){
       System.out.println(Thread.currentThread().getPriority());   // o/p-5   //main thread executes this line
       Thread.currentThread().setPriority(7);     //main thread executes this line
       MyThread myThread =new MyThread();     //main thread executes this line
       myThread .start();                                        //main thread executes this line
       System.out.println(Thread.currentThread().getPriority()); //o/p-7  //main thread executes this line
       System.out.println(myThread.getPriority()); //o/p-7  //main thread executes this line
      }
}

In above program myThread.getPriority() is 7 because main thread priority is 7. Main thread is parent thread of child thread because main thread creates child thread. So priority will be inherited from parent thread.

In this case both thread has same priority. So we can't except execution order and exact output

public class MyThread extends Thread{
     public void run(){
        for(int i=0;i<5;i++)
         {
             System.out.println("Child Thread");
        }
}
public class DemoThread{
     public static void main(String []args)
    {
        MyThread myThread=new MyThread();
        myThread.setPriority(10);
        myThread .start(); 
       for(int i=0;i<5;i++)
         {
             System.out.println("Main Thread")
        }
     }
}

In above program main thread priority is 5 and child thread priority is 10;

So child thread will run first after main thread will run.

The o/p-->"Child Thread"(5 times) followed by "Main Thread"(5 times)

Some platforms won't provide proper supports for thread priorities.

The methods to prevent Thread executions

We can prevent a thread execution by using the following methods.

1.yield() 2.join() 3.sleep()

** 1.yield()**

  • Yield() method causes to pause current executing thread to give the chance for waiting thread of same priority.

  • If there is no waiting thread or all waiting thread have low priority than same thread can continue its execution.

  • If multiple threads are waiting with same priority than which waiting thread will get the chance we can't except ,it depends on thread on thread scheduler.

  • The thread which is yielded(paused),when it will get the chance once again, it depends on thread scheduler and we can't except exactly.

prototype of yield method

public static native void yield();

In thread life cycle ,at running state if yield() method called, the state of the thread go to ready state.

public class MyThread extends Thread{
     public void run(){
        for(int i=0;i<5;i++)
         {
             System.out.println("Child Thread");
             Thread.yield();
        }
}
public class DemoThread{
     public static void main(String []args)
    {
        MyThread myThread=new MyThread();
        myThread .start(); 
       for(int i=0;i<5;i++)
         {
             System.out.println("Main Thread")
        }
     }
}
  • In the above program we we commenting Thread.yield() ,then both thread will be executed simultaneously and we can't except which thread will complete first.

  • if we not commenting Thread.yield(),then child thread always calls yield method because of that main thread will get chance more no of times and the chance of completing main thread first is high.

  • Some platforms won't provide proper support for yield().

2.join()

  • If a thread wants to wait until completing some other thread then we should go for join().

  • For example if a thread t1 wants to wait until completing t2,then t1 has to call t2.join(). If t1 executes t2.join(),then immediately t1 will be enter into waiting state until t2 completes. Once t2 completes, then t1 can continue its execution.