MultiThreading in Java

Multi Tasking :

multitasking allows several activities to occur concurrently on the computer

  • Process-based multitasking

    Allows programs(ie processes) to run concurrently on the same computer.

    Eg : ie working on MS paint while working on word

  • Thread based multitasking

  • Allows part of the same program to run concurrently on the computer

    Eg : Ms word printing and formatting text at same time

Thread vs Process

  • Two threads share the same address space

  • context switching between the threads is usually less expensive than between process

  • The cost of communication between threads is very low

Why Multithreading

In a single-threaded Environment only one task is performed at a time

  • CPU cycles are wasted , for example when waiting for the user input

  • Multi tasking allows idle CPU time to be in good use

Thread :

A thread is an independent sequential execution with in a program

  • Many Threads can run concurreently with in a program

  • At run time threads in a program share common memory space. can therefore share both data and code (i e they are light weight compared to proces

The Main Thread:

When a stand-alone Application is run, a user thread is automatically created to execute the main method of the Application. This Thread is called Main Thread

if no user threads are spawned . the program got terminated when the main method finishes executing

All other Threads are called Child Threads Spawned from the main Thread

The main method then can finish but the Program will keep running untill all user threads have completed

The running enviroment distinguishes between user thread and daemon thread

Calling the setDaemon(boolean) method marks the status of the Thread as either Daemon or User. but this must be done before the thread has started

As long as user thread is alive JVM does not terminate

A Daemon thread is at the mercy of runtime system . if it is stopped if there are no more user threads are running. Thus terminating Program

Thread Creation :

A thread in java is represented by object of thread class

creation of the thread can be done in two ways

  1. Implementing Java.lang.Runnable

  2. Extending Java.lang.Thread class

Creating a Thread in Java

  1. By extending the Thread Class
class Multithread extends Thread{  
public Multithread(String str){
   super(str)
}
public void run(){  
    for(int i=0;i<10;i++){
        System.out.println("thread is running..."+Thread.currentThread().getName()); 
    } 
}  
public static void main(String args[]){  
    Multithread t1=new Multithread("Thread 1");  
    t1.start();  
 }  
}
  1. Implementing the Runnable Interface
class Multithreading implements Runnable{  
public void run(){  
    for(int i=0;i<10;i++){
    System.out.println("thread is running...");  
   }
}  

public static void main(String args[]){  
    Multithreading thread=new Multithreading();  
    Thread t1 =new Thread(thread);  // Using the constructor Thread(Runnable)  
    t1.start();  
 }  
}

Daemon Thread :

The Thread which executed in Background is called Daemon Thread. once user Thread got executed completely. Then it was at the mercy of the JVM to continue execution or not for Daemon Thread

class MyThread extends Thread{
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("I am in chaild Threa"+i);
        }
    }
}
public class Main
{
    public static void main(String[] args) {
        System.out.println("Hello World");
        System.out.println("I am in Main Thread");
        MyThread obj=new MyThread();
        obj.setDaemon(true);
        obj.start();        
    }
}

Which is the best method to create a Thread and Why

Creating a thread by implementing a Runnable Interface is preferred because java does not support multiple inheritance so by implementing interface we can achieve multiple interface

Implementation of run method using Lambda Expression

public class Main
{
    public static void main(String[] args) {
        System.out.println("Hello World");
        Runnable obj= ()->System.out.println("I am in Thread class using Anonymous inner class....");
        Thread thread =new Thread(obj);
        thread.start();
    }
}

Synchronization :

  1. Threads share the common memory space i e they can share the resources

  2. How ever there are critical situations only one thread should access the resource

class Stack
{
    int capacity;
     private int arr[];
     private int stackTop;
     Object lock;
     public Stack(int capacity){
         arr=new int[capacity];
         stackTop=-1;
         lock=new Object();
     }
     public synchronized boolean push(int element){

         if(isFull())
            return false;
        ++stackTop;
        try{
            Thread.sleep(1000);
        }catch(Exception e){

        }
        arr[stackTop]=element;
        return false;
     }

     public synchronized int pop(){
         if(isEmpty()){
             return Integer.MIN_VALUE;
         }
         int obj=arr[stackTop];
         try{
            Thread.sleep(1000);
        }catch(Exception e){

        }
        stackTop--;
        return obj;

     }

     public boolean isEmpty(){
         return stackTop<0;
     }

     public boolean isFull(){
         return stackTop==10;
     }


}
public class Main
{
    public static void main(String[] args) {
        Stack stack = new Stack(10);
         System.out.println("Main Thread is Starting");
         new Thread(()->{
             int counter=0;
             while(counter<10){
                 System.out.println("Pushing the element"+stack.push(counter));
                 counter++;
             }
         },"Pusher").start();

         new Thread(()->{
             int counter=0;
             while(counter<10){
                 System.out.println("Popping the element"+stack.pop());
                 counter++;
             }
         },"Pusher").start();
    }
}

Rules of Synchronization :

  • A thread must acquire an object lock associated with a shared resource before it enters the shared resource

  • A run time system ensures that no other thread enters the shared resource if another thread already holds the object lock

  • If a thread cannot immediately acquire the object lock. it is blocked ie it must wait for the lock available

  • Synchronization of static method in a class is independent of instance methods of the object class

Race condition :

It occurs when two or more threads simultaneously update the same value (stackTopIndex) as a state, as a consequence leave the value in an undefined or inconsistent state.

The Singleton class :

Only one object is created for that class , if we try to create another object it will return the same object n if its already created, constructor of the class should be private .

class TvSet
{
    private static volatile Tvset tvSetInstance=null;
     private TvSet(){
        System.out.println("I am in Private constructor");
    }
    public static TvSet getTvSetInstance(){
        if(tvSetInstance==null){
              Synchronized(TvSet.class){
                     if(tvSetInstance==null){
                       tvSetInstance=new TvSet();
                     }
                }  
        }
       return tvSetInstance;
    }
}

Volatile Key Word :

if a variable is shared by multiple threads leading to inconsistency, Basically every thread has its own cache. it reads the value from the cache instead of the main memory. for suppose if there is a common variable between the 2 threads if one thread updates the value in its cache. it will reflect in the main memory as well as another thread cache. it will solve the Problem of inconsistency, which can be explained help of the diagram

Producer Consumer Pattern :

  1. we can push items into Queue if there is some empty shells in it

  2. We can pop items from the Queue if there are any items left in the Queue

it can be explained in below code:

public class BlockingQueue
{
      private Queue<Integer> queue;
      private int capacity;
      public BlockingQueue(int cap){
        q=new LinkedList<>();
        capacity=cap;
       }
        public boolean add(int item){
            synchronized(queue){
                while(queue.size()==capacity){
                   try{
                        queue.wait();   // adder 1 and adder2 
                    }catch(){
                    }
                }
                  queue.add(item);
                  queue.notifyAll();
            }
        }
       public int remove(){
          synchronized(queue){
                 while(queue.size()==0){
                   try{
                        queue.wait();    
                    }catch(){
                    }
                }
               int element=queue.poll();
               queue.notifyAll();
               return element;
            }
       }

}

in the Above code actually threads compete for the common code. in that process suppose if that thread got a chance to execute push method first it will check whether the queue is empty or not. If it's empty it will add and notify other threads or else it will go to wait state now other threads got unblocked and again compete for the common code and vice versa

Thread States

New State: When an instance of a thread class is created then the thread said to be in new state

Runnable State : When a start method is created on new born thread then the Thread is said to be in a Runnable state

In the runnable state, the thread is ready for execution and is waiting for the availability of the processor (CPU time). There are many threads that are ready for execution, they all are waiting in a queue (line).

Running State: Running means Processor (CPU) has allocated time slot to thread for its execution. When the thread scheduler selects a thread from the runnable state for execution, it goes into the running state.

Blocked state: A thread is considered to be in the blocked state when it is suspended, sleeping, or waiting for some time in order to satisfy some condition.

Dead State : When the thread completes its run method its said to be in Dead state