Java Multithreading and Concurrency Interview Questions and Answers with Example

Java Multithreading and Concurrency Interview Questions and Answers


Java Multithreading and Conncurrency Interview questions answers for freshers and experienced.  Java Threads is popular Interview topic. lets start with how many ways Threads can be created in java.

Question 1. What are Threads in Java?

In Java, when a program requires more than one task to execute in parallel, say for example,
  1. Reading a data from a local file.
  2. Reading a data from remote connection.
When both of above task need to be executed in parallel at that time Threading will come in picture.
So Java Threads helps creating multiple independent path of execution within a program which can run parallely.  


    Question 2. How many ways Threads can be created in java?

    There is only one way by which a Thread can be created in java using java.lang.Thread class as shown below,
    Thread thread1 = new Thread();
     
    After creating a Thread object, a separate independent path is created but what task this independent path will execute? How many ways are there to assign task to a thread?
    There are mainly 3 ways by which Task can be assigned to a Thread either by,
    1. java.lang.Runnable 
    2. java.lang.Thread class itself.
    3. java.util.concurrent.Callable Interface.
    Let's see complete example of creating a Thread and assigning task to it using,

    1. Runnable interface.

    class ThreadDemo{
     public static void main(String[] args) {
      
      //Lets create Task first to assign it to the Thread
      ThreadTask threadTask = new ThreadTask();
      
      //Lets create a Thread and assign task to it.
      //Way to assign task to a Thread is by passing task object(Runnable) to Thread's constructor.
      Thread thread1 = new Thread(threadTask);
      
      //Start a thread
      thread1.start();
     }
    }
    
    class ThreadTask implements Runnable{
        @Override
        public void run() {
         //Code present here will be executed in separate independent path.
        }
    }
    
    2. Thread class
    class ThreadDemo extends Thread{
     
     @Override
     public void run() {
       //Code present here will be executed in separate independent path.
     }
     
     public static void main(String[] args) {
      
      //Lets create Task first to assign it to the Thread
      ThreadDemo threadTask = new ThreadDemo();
      
      //Lets create a Thread and assign task to it.
      //Way to assign task to a Thread is by passing task object(Runnable) to Thread's constructor.
      Thread thread1 = new Thread(threadTask);
      
      //Start a thread
      thread1.start();
     }
    }
    
    
    3. Callable interface
    class ThreadDemo{
     public static void main(String[] args) {
      
      //Create a Thread Pool of size 2 (2 here is number of threads in Thread pool.)
      ExecutorService executorService = Executors.newFixedThreadPool(2);
      //After creating a pool, it internally starts a Thread, so no need to explicitly start a thread as we did in other approach.
      //Remember only Threads are started but what task they will execute that will be passed to thread using submit() method.
      //In this approach threads will be created and started but they will wait for task to be assigned to them.     
      
      //Create Task to assign it to Threads present in Thread pool.
      ThreadTask threadTask = new ThreadTask();
      
      //Submit a task to Threads present in Thread pool. 
      Future<Result> resultObject = executorService.submit(threadTask);
      //Once a task is submitted to submit method, one of the Thread from the pool will pick the task and execute run method of task.
      //Wait for the result Object(resultObject) that will be returned by Thread after task execution.
      
      Result result = null;
      try {
       //get method will be blocked until the Thread doesn't complete it work and return a result
       result = resultObject.get();
      } catch (InterruptedException e) {
       e.printStackTrace();
      } catch (ExecutionException e) {
       e.printStackTrace();
      }
      
      System.out.println(result.code + " " + result.message);
      executorService.shutdown();
     }
    }
    
    class ThreadTask implements Callable<Result> {
     
     //method where the thread execution takes place
     public Result call() {
      //Code present here will be executed in separate independent path.   
      Result response = new Result();
      response.code = 200;
      response.message = "SUCCESS";
      return response;
     }
     
    }
    
    class Result{
     public int code;
     public String message;
    }
    
       
    So to summarize the answer, There is 1 way to create a Thread but task can be assigned to Thread using 3 different ways either by using.
    1. Runnable interface (run() method will be invoked)
    2. Thread class (run() method will be invoked)
    3. Callable interface (call() method will be invoked)


    Question 3.
    For starting a Thread we call thread.start() which internally invokes run() method. What if we call run() method directly without using start() method ?

    To answer this question one should know the purpose of start method and how Threading works internally.

    When a start() method is invoked, it internally invokes start0, which is a native method call.
    private native void start0();
    
    The basic purpose of start() method is to instruct running Operating System to create a new Thread which can be executed independently of Thread created it.

    when start() method is invoked, Thread will be created and it executes run() method of Task submitted.

    By calling thread.run() directly, will not create a new Thread instead it will call run method of the task submitted on the same caller thread.
    So there will be no separate execution for newly created Thread.


    Question 4. Can we Start a thread twice ?

    No. Thread cannot be started twice. If you try to do so, IllegalThreadStateException will be thrown.
    thread.start() can be called only once.
    class ThreadDemo{
     public static void main(String[] args) {
      
      Thread thread1 = new Thread(new Runnable() {
       public void run() {
        System.out.println("Inside run.");
       }
      });
      thread1.start();
      thread1.start();
     }
    }
    
    
    Output: Inside run. Exception in thread "main" java.lang.IllegalThreadStateException 

    How it throws IllegalThreadStateException?
    If you see the code of start() method, you will observe that Thread maintains threadStatus whose value initialy is 0 and once the thread is completed its value is 2.
     private volatile int threadStatus = 0;
     public synchronized void start() {
           
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
            }
            ....
            ....    
    }
    
    So when thread.start() is called again, threadStatus value is 2 and not 0 that is why it throws IllegalThreadStateException .




    Question 5.
    Can main thread dies before the child thread?
    Does child threads still executes even after if their parent thread dies or terminates?
    Will JVM exits after main thread is dead?

    First of all I would like to tell you that there is no concept of parent - child relationship between threads.
    Each and every thread created is independent of thread who created it.

    Now coming back to actual question, Can main Thread dies before the child thread? Yes. 
    Main thread dies after completing its job even after the thread created by main thread is not yet completed.

    But point is will JVM die or not. 
    If there exist any non-daemon thread in JVM which is not yet completed then JVM will not exit and wait until all no non-daemon threads completes its task.
    In other words, we can also say, JVM will exit when the only running threads are daemon threads.

    Lets see example below and things will be more clear,
    public class ThreadDemo {
     public static void main(String ar[]){
    
      final Thread mainThread = Thread.currentThread();
      System.out.println("Inside Main Thread :"+mainThread.getName());
    
      new Thread(new Runnable() {
    
       @Override
       public void run() {
        Thread childThread= Thread.currentThread();
        for(int i=0; i<5;i++){
         System.out.println("Inside Child Thread :"+childThread.getName());
         try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        }
        System.out.println("Check Main Thread is alive :" + mainThread.isAlive());
       }
    
      }).start();
    
      System.out.println("End of Main Thread");  
     }
    }
    
    
    


    Question 6.
    Is there any relationship between threads like parent-child?

    No. Thre is no relationship between Threads like parent or child thread. Once thread is created it is totaly separate independent thread from the thread who created it. 

    There is no relationship between Thread newly created and Thread who created it except for Thread priority and Daemon property.

    Thread priority and Daemon property of Thread is copied to the newly created thread from the thread created it.

    To put it in simple terms, When you start a thread it inherits the,
    1. Thread daemon property and 
    2. Thread priority
    from the "parent" thread to "child" thread and that is the only relationship between threads and no other relation exist after thread starts.

    Lets see with simple example,

    public class ThreadDemo{
     
     public static void main(String ar[]){
      System.out.println("Inside Main Thread");
      
      Thread thread = new Thread(new ThreadTask());
      thread.setDaemon(true);
      thread.start();
      
      try {
       Thread.sleep(5000);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("End of Main Thread");  
     }
    }
    
    class ThreadTask implements Runnable{
    
     @Override
     public void run() {
      System.out.println("Inside Thread Task start");
      
      new Thread(new Runnable() {
       
       public void run() {
        Thread childThread = Thread.currentThread();
        while(true){
         System.out.println("Inside Child Thread :"+childThread.getName());
         try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        }
       }
    
      }).start();
      
      System.out.println("Inside Thread Task end");
     }
     
    }
    
    

    Output:
    Inside Main Thread
    Inside Thread Task start
    Inside Thread Task end
    Inside Child Thread :Thread-1
    Inside Child Thread :Thread-1
    Inside Child Thread :Thread-1
    Inside Child Thread :Thread-1
    Inside Child Thread :Thread-1
    End of Main Thread


    After main thread completed, JVM terminates even if there was 2 threads present one was Daemon thread and other thread inherited Daemon property from thread created it. 





    Question 7.
    What is the use of join method in case of threading in java?

    join() method is used for waiting the thread in execution until the thread on which join is called is not completed.
    Remember, the thread which will wait is the thread in execution and it will wait until the thread on which join method called is not completed.

    Lets take a scenario, we have Main thread, Thread 1, Thread 2 and Thread 3 and we want our thread to execute in particular scenario like,
    Main thread to start first and ends only after all 3 threads is completed.
    Thread 1 to start and complete.
    Thread 2 to start only after Thread 1 is completed.
    Thread 3 to start only after Thread 2 is completed.

    Let's see program for it.
    public class ThreadDemo {
     
     public static void main(String ar[]){
      System.out.println("Inside Main Thread");
      
      Thread thread1 = new Thread(new ThreadTask());
      thread1.start();
      
      Thread thread2 = new Thread(new ThreadTask(thread1));
      thread2.start();
      
      Thread thread3 = new Thread(new ThreadTask(thread2));
      thread3.start();
       
      try {
       thread1.join();
       thread2.join();
       thread3.join();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("End of Main Thread");  
     }
    }
    
    class ThreadTask implements Runnable{
    
     public ThreadTask() {}
     
     public ThreadTask(Thread threadToJoin) {
      try {
       threadToJoin.join();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
     
     @Override
     public void run() {
      System.out.println("Start Thread :"+Thread.currentThread().getName());  
      try {
       Thread.sleep(5000);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("End Thread :"+Thread.currentThread().getName());
     } 
    }
    
    
    Output:
    Inside Main Thread
    Start Thread :Thread-0
    End Thread :Thread-0
    Start Thread :Thread-1
    End Thread :Thread-1
    Start Thread :Thread-2
    End Thread :Thread-2
    End of Main Thread



    Question 8.
    How join method works internally in java?

    There is complete detailed post on this, Please go through it for answer. 


    Question 9.
    When join method is invoked, does thread release its resources and goes in waiting state or it keep resources and goes in waiting state?

    If you look at source code of join() method, It internally invokes wait() method and wait() method release all the resources before going to WAITING state.
    public final synchronized void join(){
        ...
        while (isAlive()) {
            wait(0);
        }
        ...
    }
    
    So, YES. join() method release resources and goes to waiting state.

    Let's see sample program and understand,
    class ThreadJoinDemo extends Thread{
     static ThreadJoinDemo thread1;
    
     public void run(){
      try{
       synchronized(thread1){
        System.out.println(Thread.currentThread().getName()+" acquired a lock on thread1");
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName()+" completed");
       }
      }
      catch (InterruptedException e){ }
     }
    
     public static void main(String[] ar) throws Exception{
      thread1 = new ThreadJoinDemo();
      thread1.setName("thread1");
      thread1.start();
    
      synchronized(thread1){
       System.out.println(Thread.currentThread().getName()+" acquired a lock on thread1");
       Thread.sleep(1000);
       thread1.join();
       System.out.println(Thread.currentThread().getName()+" completed");
      }
     }
    }
    
    
    
    1. If you see the code, "main" thread took a lock on Thread "thread1" and waits for thread1 to complete its task by calling thread1.join().
    2. Thread "thread1", require a lock on "thread1" for executing its task.
      If main thread doesn't release lock by calling thread1.join() then Thread "thread1" will not able to progress and goes in deadlock state.


    Question 10.
    What is the practical use of join() method?

    Suppose we want to calculate the population of country and based on population number further action need to be taken. 
    we can break down this problem as calculating population of each state in a country. 
    What we can do is, if country has "n" states, we can create "n" threads(+1 main thread), each calculating population of different states.
    Now main thread can't do further action until all the state thread update population result.
    So we can join all state threads on main thread, So that main thread will wait for all state thread to complete and once result from all state thread is available it can make progress for further actions.

    Note: there can be many other ways to solve this problem. 

    Question 11.
    Can Thread be created without any ThreadGroup, I mean can Thread exist independently without associated to any ThreadGroup?

    No. Thread cannot be created independently, it will be part of atleast one of the Thread Group.

    Generally, while creating Thread we do not associate it to any Thread Group, but internally it will be part of "main" Thread Group.

    So let's see how ThreadGroup hierarchical structure is, 
    Threads / Thread Groups, which are directly created within main thread will be part of "main" thread group and will be parallel to main thread.

    What will be output of below lines?
    public static void main(String[] args) {
     System.out.println("Top Level Thread Group:" + Thread.currentThread().getThreadGroup().getParent().getName());
     System.out.println("Main Thread Group:" + Thread.currentThread().getThreadGroup().getName());
    }
    
    Output:Top Level Thread Group: system
    Main Thread Group: main 


    Question 12.
    Say Thread "t1" is spawned from "main" thread, what happen when RuntimeException is thrown from "t1", Will "main" thread continue to run?

    Yes. "main" thread will continue to run if an exception is thrown from threads that are created within main thread.

    Let's see example and understand,
    class ThreadDemo{
     public static void main(String[] args) {
      
      Thread t1 = new Thread(new Runnable() {
       @Override
       public void run() {
        throw new RuntimeException("Thread Exception Example");
       }
      });
      t1.start();
      
      while(true){
       System.out.println("ThreadDemo.main()");
       try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
      }
     }
    }
    

    In above example, RuntimeException thrown by Thread "t1" will not affect "main" thread and it continues to print "ThreadDemo.main()"

    In general, Exception thrown by one thread will not affect another thread, as all threads are independent and have different stack.


    Question 13.
    How exceptions are handled in case of Multithreading scenario? who will handle Exceptions if there is no handler?

    Exceptions that are thrown from Thread can be handled in 3 different ways,

    1. At Thread Level
    Each threads have there own Exception handling mechanism and can be caught and configured in the way shown below,
      
    Thread t1 = new Thread(new WorkerThread());
    t1.setName("T4");
    
    t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
            @Override
     public void uncaughtException(Thread t, Throwable e){
        System.out.println("Thread Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
            }
    });
    t1.start();
    
    
     
    class WorkerThread extends Thread {
     public void run() {
      throw new RuntimeException("RuntimeException");
     }
    }
    
    2. At ThreadGroup Level
    Each ThreadGroup have there own Exception handling mechanism which will be applicable to all thread within group and can be caught and configured in the way shown below,
    ThreadGroup tr = new ThreadGroup("MyGroup"){
     @Override
     public void uncaughtException(Thread t, Throwable e) {
      System.out.println("ThreadGroup Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
     }
    };
    
    Thread t1 = new Thread(tr, new WorkerThread());
    t1.setName("T1");
    t1.start();
    
    
    2. At Global Thread Level
    Default Exception Handler can be configured at Global thread level which will be applicable to all the threads,
    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){     
     @Override
     public void uncaughtException(Thread t, Throwable e){
      System.out.println("Default Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
     }
    });
    

    When an uncaught exception occurs from a particular Thread, the JVM looks for handler in way shown below,
    1. First JVM will look whether UncaughtExceptionHandler (setUncaughtExceptionHandler) for the current Thread is set or not.

      If set than exception will be catch by Thread handler.
      If not set than exception will be propagated up the call stack.

    2. Second JVM will check whether uncaughtException of ThreadGroup is overriden or not,
      JVM will not only check uncaughtException handler of direct ThreadGroup of which Thread is part of, but JVM will also look at all the parent ThreadGroups
      as well.

      If
      uncaughtException is overriden by any one of ThreadGroup handler than exception will be caught by that ThreadGroup handler.
      If not set than exception will be propagated up the call stack.

    3. Third JVM will check whether DefaultUncaughtExceptionHandler (setDefaultUncaughtExceptionHandler) at JVM level(Global Thread level) is configured or not, It will act as handler for all the Threads in JVM.

      If set than exception will be catch by Global Thread handler.

      If not set than exception will be propagated up the call stack.

    4. When there is no handler configured, Threadgroup class ("main" threadgroup to which main thread is part of) provide default implementation of uncaughtException() method that is called which prints Exception as shown below and JVM shutdowns. 
    System.err.print("Exception in thread \"" + t.getName() + "\" "); 
    

    Lets understand with help of example:


    1. Global Exception handler for the application (JVM)
    class ThreadDemo{
     public static void main(String[] args) {
    
      //Register Global Exception Handler for all Threads
      Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){     
       @Override
       public void uncaughtException(Thread t, Throwable e){
        System.out.println("Default Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      });
      
      Thread t1 = new Thread(new Runnable() {
       @Override
       public void run() {
        //Exception from New Thread spawned from "main" thread
        throw new RuntimeException("I am RuntimeException");
       }
      });
      t1.start();
    
      while(true){
       try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
       
       //Exception from main thread
       throw new RuntimeException("I am RuntimeException");
      }
      
     }
    }
    
    
    Output:
    Default Exception Handler :Thread Name :Thread-0 Message :I am RuntimeException
    Default Exception Handler :Thread Name :main Message :I am RuntimeException
    

    2. ThreadGroup Exception handler for all Threads within ThreadGroup.
    class ThreadDemo{
     public static void main(String[] args) {
      
      //Register ThreadGroup Exception Handler for all Threads that are part of ThreadGroup.
      ThreadGroup tr = new ThreadGroup("MyGroup"){
       @Override
       public void uncaughtException(Thread t, Throwable e) {
        System.out.println("ThreadGroup Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      };
    
      Thread t1 = new Thread(tr, new Runnable() {
       @Override
       public void run() {
        throw new RuntimeException("I am RuntimeException");
       }
      });
      t1.setName("T1");
      t1.start();
    
      Thread t2 = new Thread(tr, new Runnable() {
       @Override
       public void run() {
        throw new RuntimeException("I am RuntimeException");
       }
      });
      t2.setName("T2");
      t2.start();
      
     }
    }
    

    Output:
    ThreadGroup Exception Handler :Thread Name :T1 Message :I am RuntimeException
    ThreadGroup Exception Handler :Thread Name :T2 Message :I am RuntimeException
    
    3. Thread level Exception handler for particular Thread.
    class ThreadDemo{
     public static void main(String[] args) {
      
      Thread t1 = new Thread(new Runnable() {
       @Override
       public void run() {
        throw new RuntimeException("I am RuntimeException");
       }
      });
    
      t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
       @Override
       public void uncaughtException(Thread t, Throwable e){
        System.out.println("Thread Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      });
      t1.start();
     }
    }
    

    Output:
    Thread Exception Handler :Thread Name :Thread-0 Message :I am RuntimeException
    

    Question 14.
    If one Thread throws RuntimeException will complete application(other running threads) goes down?

    No. Only thread from which the exception is occured will terminate.
    Other thread will continue to run and progress if an exception is thrown from one thread. 

    Let's see example and understand,
    class ThreadDemo{
     public static void main(String ar[]){
    
      //Register Global Exception Handler for all Threads
      Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){     
       @Override
       public void uncaughtException(Thread t, Throwable e){
        System.out.println("Default Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      });
    
      //Register ThreadGroup Exception Handler for all Threads in ThreadGroup
      ThreadGroup tr = new ThreadGroup("MyGroup"){
       @Override
       public void uncaughtException(Thread t, Throwable e) {
        System.out.println("ThreadGroup Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      };
    
      Thread t1 = new Thread(tr, new WorkerThread());
      t1.setName("T1");
      t1.start();
    
      Thread t2 = new Thread(tr, new WorkerThread());
      t2.setName("T2");
      t2.start();
    
      Thread t3 = new Thread(new WorkerThread());
      t3.setName("T3");
    
      t3.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
       @Override
       public void uncaughtException(Thread t, Throwable e){
        System.out.println("Thread Exception Handler :Thread Name :"+t.getName() + " Message :"+e.getMessage());
       }
      });
      t3.start();
    
      Thread t4 = new Thread(new WorkerThread());
      t4.setName("T4");
      t4.start();
      
      Thread t5 = new Thread(new Runnable() {
       @Override
       public void run() {
        while(true){
         try {
          System.out.println("I am printing");
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        }
       }
      });
      t5.setName("T5");
      t5.start();
      
     }
    }
    
     
    
    In above example, RuntimeException thrown by Thread "t1", "t2", "t3", and "t4" will not affect thread "t5" which prints "I am printing" and it continues to print even after exception is thrown from other threads.

    In general, Exception thrown by one thread will not affect another thread, as all threads are independent and have different call stack. So exception from one thread will propagate up untill the handler is not found and if no handler is configured then default "main" ThreadGroup handler will be invoked for that particular thread.


    Question 15.
    How JVM handle Exceptions?

    Have a look at below diagram and you will able to understand how JVM handles Exception ,



    Question 16.
    In case of Exception, What happen to lock which is acquired by Thread, will that be released?

    When an Exception is thrown from the Thread which hold lock on some resource say "obj", Thread will release a lock on "obj", so that Thread in which Exception occured can be terminated but other threads can still progress.

    If say thread doesn't release lock on Exception, if this is the case then it may result in deadlock.
    Say Thread "thread1" is waiting for lock on "obj" to enter in synchronized block.
    Say Thread "thread2" is holding lock on "obj" and doing some operation and now if thread2 throws exception then "thread1" will be blocked and cannot progress.
     If execution of the synchronized block completes normally, then the lock is unlocked and the synchronized statement completes normally. 

    If execution of the synchronized block completes abruptly for any reason, then the lock is unlocked and the exception is thrown until it finds exception handler up the call stack.

     

    Question 17.
    What is the output of the Program? 

    class ThreadDemo{
     public static void main(String ar[]){
      System.out.println(hello());
     }
     
     private static int hello(){
      try{
       throw new RuntimeException("dwed");
      }finally{
       return 10;
      }
     }
    }
    
    Output:
    10

    Why exception is not thrown?
    When control entered hello() method, it encounters line in try block which throws RuntimeException,
     There is no handler for RuntimeException, so JVM will mark to throw exception up in call stack and go for execution of finally block.

    Finally block overwrote the JVM marked return statement to throw RuntimeException and now return from finally block is result 10 and not RuntimeException.

     

    Question 18.
    Is it possible to take a lock on null reference? What is the output of the Program? 

    class SynchronizationExample{
     private static SynchronizationExample synchronizationExample = null;
     public static void main(String ar[]){
      hello();
     }
     
     private static void hello(){
      synchronized (synchronizationExample) {
        System.out.println("Inside synchronized block");
      }
     }
    }
    
    Output:
    NullPointerException at line 8

    Lock cannot be acquired on null reference.
    calling a synchronized method on object and intrinsic acquiring a lock on that object is similar to  acquiring extrinsic lock by using synchronized() block.
    public void synchronized method1(){}
    
    calling obj1.method1() will hold a lock on obj1 (obj1 cannot be null otherwise NPE will be thrown)
    Similarly,
    public void method1(){ synchronized(obj1){}}
    
    obj1 at this point should also be not null for holding a lock on obj1.

    When a method cannot be called on null reference and it throws NullPointerException if you try to do so then how can you we acquire a lock on null refernce in synchronized block as both ways of taking lock is similar.

     

    Question 19.
    When we cannot take a lock on null reference, what will happen if we make a reference null after acquiring a lock on object it is referering to? What is the output of the Program? 

    class SynchronizationExample{
     private static SynchronizationExample synchronizationExample = new SynchronizationExample();
     public static void main(String ar[]){
      hello();
     }
     
     private static void hello(){
      synchronized (synchronizationExample) {
        System.out.println("Before making reference null");
        synchronizationExample = null;
        System.out.println("After making reference null");
      }
     }
    }
    
    Output:
    This is perfectly fine and outputs,
    Before making reference null
    After making reference null




    Question 20.
    What is the output of below program? Will synchronized block will be executed in synchronized way as hello() method is executed on different object?

    class SynchronizationExample{
     private static Object obj = new Object();
    
     public static void main(String ar[]){
      
      new Thread(new Runnable() {
       @Override
       public void run() {
        SynchronizationExample sy1 = new SynchronizationExample();
        sy1.hello();
       }
      }).start();
      
      new Thread(new Runnable() {
       @Override
       public void run() {
        SynchronizationExample sy2 = new SynchronizationExample();
        sy2.hello();
       }
      }).start();
        
     }
    
     private void hello(){
      synchronized (obj) {
       System.out.println("Thread :"+Thread.currentThread().getName() + " Inside");
       try {
        Thread.sleep(5000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
       System.out.println("Thread :"+Thread.currentThread().getName() + " Leaving");
      }
     }
    }
    
    
    Output:
    Yes, It will be executed in synchronized way because sy1.hello() and sy2.hello() both synchronize on the same STATIC object "obj" and hence execute in synchronized way.




    Question 21.
    synchronized block acquires locks on reference or object? Will call to method hello1() and hello2() will exceute in synchronized way?

    class SynchronizationExample{
     private static final Object LOCK = new Object();
     private Object lockReference1 = LOCK;
     private Object lockReference2 = LOCK;
    
     static SynchronizationExample se = new SynchronizationExample();
     
     public static void main(String ar[]){
      new Thread(new Runnable() {
       @Override
       public void run() {
        se.hello1();
       }
      }).start();
    
      new Thread(new Runnable() {
       @Override
       public void run() {
        se.hello2();
       }
      }).start();
    
     }
    
     public void hello1() {
      synchronized(lockReference1) {
       System.out.println(Thread.currentThread().getName() + " in synchronized block");
       try {
        Thread.sleep(5000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
       System.out.println(Thread.currentThread().getName() + " leaving synchronized block");
       
      }
     }
    
     public void hello2() {
      synchronized(lockReference2) {
       System.out.println(Thread.currentThread().getName() + " in synchronized block");
       try {
        Thread.sleep(5000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
       System.out.println(Thread.currentThread().getName() + " leaving synchronized block");
      }
     }
    
    }
    
    
    Output:
    Yes, It will be executed in synchronized way because lockReference1 and lockReference2 both pointing to same object (same memory location), So synchronized block acquires lock on object and not the references that is why lock on null reference in synchronized block gives NullPointerException.




    Question 22.
    For synchronizing and communicaion between threads, we use wait() and notify() method inside synchronized method/block.
    Threads acquire lock on common object and then calls wait() and notify() on same object for their communication.

    How to communicate between Threads which acquire class level lock.
    wait() and notify() are not static and is instance method, so how to use for communication between Threads which acquire class level lock?

    There are 2 types of lock for each class, 
    1. object lock
    2. Class lock
    object lock:
    Whenever a call to any instance method (getA()) is made using object obj1 in Thread t1, then t1 acquires lock on that object/instance (obj1).
    public synchronized void getA(){}

    Class lock
    Whenever a call to any Class/static method (getD()) is made using class A in Thread t1, then t1 acquires lock on that Class (A).
    public static synchronized void getD(){} 

    Both object and Class locks are different and they don't interfere with each other.

    we can create multiple object's of a class and each object will have one lock associated with it.
    When we acquire a lock on any class, we actually acquire a lock on "Class" class instance which is only one for all instances of class.

    For communication between Threads which acquire a lock on object, we call obj1.wait() and obj1.notify().
    For communication between Threads which acquire a lock on class A, we call A.class.wait() and A.class.notify().

    Let's understand it with example below,
    class ThreadDemo{
     
     public static void main(String[] args) {
      final ThreadDemo threadDemo1 = new ThreadDemo();
    
      new Thread(new Runnable() {
       @Override
       public void run() {
        threadDemo1.getA();
       }
      }).start();
      
      new Thread(new Runnable() {
       @Override
       public void run() {
        threadDemo1.getB();
       }
      }).start();
     
      new Thread(new Runnable() {
       @Override
       public void run() {
        ThreadDemo.getC();
       }
      }).start();
      
      new Thread(new Runnable() {
       @Override
       public void run() {
        ThreadDemo.getD();
       }
      }).start();
      
     }
    
     /***INSTANCE METHOD***/
     public synchronized void getA(){
      System.out.println("ThreadDemo.getA() :"+Thread.currentThread().getName() + " enetered");
      try {
       Thread.sleep(2000);
       wait();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("ThreadDemo.getA() :"+Thread.currentThread().getName() + " leaving");
     }
     
     public synchronized void getB(){
      System.out.println("ThreadDemo.getB() :"+Thread.currentThread().getName() + " enetered");
      try {
       Thread.sleep(2000);
       notify();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("ThreadDemo.getB() :"+Thread.currentThread().getName() + " leaving");
     }
     
     
     /***CLASS METHOD***/
     public static synchronized void getC(){
      System.out.println("ThreadDemo.getC() :"+Thread.currentThread().getName() + " enetered");
      try {
       Thread.sleep(2000);
       ThreadDemo.class.wait();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("ThreadDemo.getC() :"+Thread.currentThread().getName() + " leaving");
     }
     
     public static synchronized void getD(){
      System.out.println("ThreadDemo.getD() :"+Thread.currentThread().getName() + " enetered");
      try {
       Thread.sleep(2000);
       ThreadDemo.class.notify();
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
      System.out.println("ThreadDemo.getD() :"+Thread.currentThread().getName() + " leaving");
     }
     
    }
    
    
    Proper communication happens with both instance lock and Class level lock.


    Question 23.
    Can thread switching happen while thread executing inside synchronized block?

    Yes. Context switching can happen while in the synchronized block.
     
    A synchronized block doesn't block other threads from executing and it only prevents other threads from entering a block that is synchronized on the same object.
    Other threads continue running while a synchronized block is being executed.

    Context switching can happen while in the synchronized block, because other threads should also get chance to progress  

    If context switching is not allowed while executing in synchronized block, then it is no longer parallel execution and if code inside synchronized block requires much time to execute then it will block everything else.



    Question 24.
    Non volatile variables that are updated inside synchronized block by Thread t1 (synchronized block of Thread t1 is not yet completed) is guaranteed to be visible to Thread t2 and Thread t3 reading the same value? 

    If, Thread t1 changes value of "abc" variable. 
    Thread t2 may or may not read updated value of variable "abc". 
    Thread t3 is guaranteed to read updated value of variable "abc".

    Let's take a example an understand,
    class ThreadDemo {
    
     private static final Object lock = new Object();
     private String abc = "hello";
    
     final AtomicInteger i = new AtomicInteger();
    
     public void get1(){
      synchronized(lock){
       abc = "Hello :"+i.incrementAndGet();
       //Consider at this point 
                            //Thread t1 gets preempted and 
                            //Thread t2 gets executed.
       System.out.println("Changed :"+abc);
      }
     }
     public void get2(){
      System.out.println(abc);
     }
    
     public void get3(){
      synchronized(lock){
       System.out.println(abc);
      }
     }
     
     public static void main(String args[]){
      final StaticClass s = new StaticClass();
    
      new Thread(new Runnable() {
       public void run() {
        s.get1();          
       }
      }, "t1").start();
      
      new Thread(new Runnable() {
       public void run() {
        s.get2();          
       }
      }, "t2").start();
      
      new Thread(new Runnable() {
       public void run() {
        s.get3();          
       }
      }, "t3").start();
      
     }
    }
    
    
    There are two type of memory barrier instructions in Java Memory Model,
    1. read barrier.
    2. write barrier.
    Read Barrier
    A read barrier invalidates the local memory (cache, registers, etc) and then reads the contents directly from the main memory,
    So that changes made by other threads becomes visible to the current Thread executing.


    Write Barrier
    A write barrier flushes out the contents of the processor's local memory to the main memory,
    So that changes made by the current Thread becomes visible to the other threads.

    When a thread acquires monitor(lock) on object, by entering into a synchronized block of code,
    It first performs a Read Barrier (invalidates the local memory and reads from the heap instead).

    Similarly exiting from a synchronized block as part of releasing the associated monitor,
    It performs a Write Barrier (flushes changes to the main memory)


    Case 1:
    Modification of non-volatile variable in synchronized block by thread t1 is Guaranteed to be visible to other thread t2 inside synchronized block only if it also acquires lock on same monitor.
     public void get1(){
      synchronized(lock){
       abc = "Hello :"+i.incrementAndGet();
       //Consider at this point Thread t1 gets preempted and Thread t2 gets executed.
       System.out.println("Changed :"+abc);
      }
     }
    
     public void get3(){
      synchronized(lock){
       System.out.println(abc);
      }
     }
    
    

    1. Thread t1 acquires lock on monitor "lock", reaches Read barrier, reads updated value of variable 
        from memory.
    2. Changes value of variable "abc" to "Hello..", writes the value to its local cache.
    3. Thread t1 gets preempted and Thread t2 gets chance to execute and it calls a method get3().
    4. Thread t2 acquires lock on same monitor "lock", reaches Read barrier, so all value updated after 
        acquiring lock on monitor "lock" by other thread gets flushed to main memory before any read 
        happens. updated value of variable "abc" that is "Hello.." by Thread t1 gets flushed to main 
        memory first.
    5. So thread t2 reads updated value of variable "abc" inside synchronized block.

    Case 2:
    Modification of non-volatile variable in synchronized block by thread t1 is Not Guaranteed to be visible to non synchronized thread t2.

     public void get1(){
      synchronized(lock){
       abc = "Hello :"+i.incrementAndGet();
       //Consider at this point Thread t1 gets preempted and Thread t2 gets executed.
       System.out.println("Changed :"+abc);
      }
     }
    
     public void get2(){
      System.out.println(abc);
     }
    
    

    1. Thread t1 acquires lock on monitor "lock", reaches Read barrier, reads updated value of variable 
        from memory.
    2. Changes value of variable "abc" to "Hello..", writes the value to its local cache.
    3. Thread t1 gets preempted and Thread t2 gets chance to execute and it calls a method get2().
    4. Thread t2 simply reads value of variable "abc" without any synchronization on same monitor,
        So there is no read barrier and it is not guaranteed that partial update on variable "abc" by thread t1

        will be flushed to main memory and updated value may still be in thread cache
    5. So thread t2 may gets updated value of variable "abc" or may not be as it is totally dependent on 
        JVM whether it has sync thread t1 local memory to main memory or not yet.

    Values are guaranteed to be updated only when read and write barrier happens, all intermediate state of varaiables are not guaranteed to be flushed to main memory by JMM.



    Question 25.
    Why are local variables thread safe in Java? 

    Each thread will have its own stack which it uses to store local variables.
    Two threads will have two stacks and one thread never shares its stack with other thread.

    All local variables defined in method will be allocated memory in stack
    As soon as method execution is completed by this thread, stack frame will be removed.

    That means that local variables are never shared between threads.
    //i is shared across threads
    public class iIsNotThreadsafe {
        int i = 0;
        public int foo() {
            i++;
            return i;
        }
    }
    
    //Each Thread will have local copy of i in its own call stack.
    public class iIsThreadsafe {
        public int foo() {
            int i = 1;
            i++;
            return i+i;
        }
    }
    

    Post a Comment