Top 45 Java-multithreading Interview Questions You Must Prepare 19.Mar.2024

yield() method causes the currently executing thread object to temporarily pause and allow other threads to execute. If there is no waiting thread or all the waiting threads have a lower priority than the current thread, then the same thread will continue its execution. When the yielded thread will get the chance for execution is decided by the thread scheduler whose behavior is platform dependent.

Every thread has a priority, usually higher priority thread gets precedence in execution but it depends on Thread Scheduler implementation that is OS dependent. We can specify the priority of thread using Thread’s setPriority(int) method but it doesn’t guarantee that higher priority thread will get executed before lower priority thread. Thread priority is an int whose value varies from 1 to 10 where 1 is the lowest priority and 10 is the highest priority.

No, constructor cannot be synchronized. Constructor is used for instantiating object and when we are in constructor, object is under creation. So, until object is not instantiated it does not need any synchronization.

1.If we instantiate a thread it is called in new state until the Start() method is called.
2.If we don't call a start() method for that thread instance, the thread is not called alive.
3.If we invoke run method without calling the start method for a thread instance, the code in run() method wil not be executed by a new thread but it will be executed by the existing thread only.

Daemon threads are non-user threads. They are typically used to carry out low-priority tasks that should not take priority over the main task of the program. They can be used to do useful work when all other user threads are blocked. The garbage collector is one example of a daemon thread.
JVM terminates itself when all non-daemon threads (user threads) finishes their execution, JVM does not care even if some Daemon threads are running. If JVM finds running daemon thread (upon completion of user threads), it terminates the thread and after that shutdowns itself. You can make a user thread to Daemon by using setDaemon() method of thread class.
A child thread created from daemon thread is also a daemon thread.

Timeslicing is the method of allocating CPU time to individual threads in a priority schedule.

yield() method pauses the currently executing thread temporarily for giving a chance to the remaining waiting threads of the same priority to execute. If there is no waiting thread or all the waiting threads have a lower priority then the same thread will continue its execution. The yielded thread when it will get the chance for execution is decided by the thread scheduler whose behavior is vendor dependent. If doesn't release the lock on the objects acquired.
sleep() allows the thread to go to sleep state for x milliseconds. When a thread goes into sleep state it doesn’t releases the lock.

  • In this method of creating thread, we have to implement the Runnable interface and implement the run() method in our class.
  • We have to create an object of our class.
  • Then we you have to pass the reference of that object for creating a new object of Thread
  • Invoke the start method using this Thread object which will create a new thread of execution.
    For example
    public class MyThread implements Runnable
    {
      public void run()
      {
          // code to execute under the thread
      }
      public static void main(String [] args)
      {
          MyThread c = new NewThread();
          Thread t = new Thread(c);
          t.start();
      }
    }

When a code running in a thread creates a new thread object, the priority of the new thread is set equal to the priority of the thread which has created it.

The methods of the thread class used to schedule the threads are as follows:

  •  public final void join() throws InterruptedException
  •  public final void notify()
  •  public final void notifyAll() 
  •  public static void yield()
  •  public final void setPriority(int priority)
  •  public static void sleep(long millis) throws InterruptedException
  •  public final void wait() throws InterruptedException

By default a thread created in a Java program is always a user thread however we can make it daemon by calling setDaemon(true) method, if needed. A daemon thread runs in the background and doesn’t prevent JVM from terminating. As soon as all user thread finishes execution, Java program or JVM terminates itself, JVM doesn’t wait for daemon thread to finish their execution. As soon as last non daemon thread finished, JVM terminates no matter how many Daemon thread exists or running inside JVM.

volatile is a special modifier which is used to indicate that a variable’s value will be modified by different threads. The volatile keyword will mark a Java variable as “being stored in main memory”. The value of this variable will never be cached locally: all reads and writes will go straight to “main memory”. Volatile variable guarantees that a write will happen before any subsequent read. Access to the variable acts as though it is enclosed in a synchronized block.

As memory is shared between different threads, ThreadLocal provides a way to store and retrieve values for each thread separately. Implementations of ThreadLocal store and retrieve the values for each thread independently such that when thread A stores the value A1 and thread B stores the value B1 in the same instance of ThreadLocal, thread A later on retrieves value A1 from this ThreadLocal instance and thread B retrieves value B1.

Collections provide the method parallelStream() to create a stream that is processed by a thread pool. Alternatively you can call the intermediate method parallel() on a given stream to convert a sequential stream to a parallel counterpart.

A prominent example for such optimizations is a List implementation that holds the number of elements as a separate variable. This improves the performance for single-threaded applications as the size() operation does not have to iterate over all elements but can return the current number of elements directly. Within a multi-threaded application the additional counter has to be guarded by a lock as multiple concurrent threads may insert elements into the list. This additional lock can cost performance when there are more updates to the list than invocations of the size() operation.

The different states of threads are as follows:
New – When a thread is instantiated it is in New state until the start() method is called on the thread instance. In this state the thread is not considered to be alive.
Runnable – The thread enters into this state after the start method is called in the thread instance. The thread may enter into the Runnable state from Running state. In this state the thread is considered to be alive.
Running – When the thread scheduler picks up the thread from the Runnable thread’s pool, the thread starts running and the thread is said to be in Running state.
Waiting/Blocked/Sleeping – In these states the thread is said to be alive but not runnable. The thread switches to this state because of reasons like wait method called or sleep method has been called on the running thread or thread might be waiting for some i/o resource so blocked.
Dead – When the thread finishes its execution i.e. the run() method execution completes, it is said to be in dead state. A dead state can not be started again. If a start() method is invoked on a dead thread a runtime exception will occur.

  • In Preemptive scheduling, highest priority task will executes until it enters in waiting or dead states. It also executes, until a higher priority task enters.
  • In Time slicing, a task will execute for a fixed time slice and after that it will go in ready state.
  • At that time the scheduler will find the executable task, according to the priority and various other tasks.
  • In preemptive scheduling, the running task will be preempted by the higher priority task.
  • In time slicing methods, a task executes until the specified period of time. Once the execution of that task is complete then the higher priority task will be executed if available.

We can use join() method to ensure all threads that started from main will end in order in which they started and also main should end in last.

The process of executing multiple threads simultaneously is known as multithreading. Java supports multithreading. The main advantage of multithreading is reducing CPU idle time and improving the CPU utilization. This makes the job to be completed in less time.

When a synch non static method is called a lock is obtained on the object. When a synch static method is called a lock is obtained on the class and not on the object. The lock on the object and the lock on the class don’t interfere with each other. It me, a thread accessing a synch non static method, then the other thread can access the synch static method at the same time but can’t access the synch non static method.

  • Sleep causes the currently executing thread to sleep until the specified time is completed. The thread will resume once the specified time period is over.
  • Sleep causes the currently executing thread to sleep and gives a chance to other threads to execute. The thread will join the ready queue.
  • Thread.sleep() will moves the thread to “Wait” state.
  • Thread.yield() will moves the thread to “Ready” state.

When an unchecked exception has occurred in the run() method, the thread is stopped by the Java Virtual Machine. It is possible to catch this exception by registering an instance that implements the interface UncaughtExceptionHandler as an exception handler.
The handler can be registered by invoking the static method Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) or by invoking setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler) on the thread instance which tells the JVM to use the provided handler in case there was no specific handler registered on the thread.

notify() method wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is random and occurs at the discretion of the implementation. notifyAll() wakes up all threads that are waiting on this object’s monitor. A thread waits on an object’s monitor by calling one of the wait methods.

Following are some way to debug issues in multi-threaded applications in Java.

  •  By using logging and print statements along with thread names. In this way we can know about the flow of thread execution.
  •  With the use of debugging functionality available in Eclipse and JDeveloper.
  •  We can write a thread dump of the application which will give the information about the active threads at a point of time.
    This is most effective way for detecting deadlocks in production systems.

In programming, an atomic operation is one that effectively happens all at once. An atomic operation cannot stop in the middle: it either happens completely, or it doesn’t happen at all. No side effects of an atomic operation are visible until the action is complete.
In Java,

  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and double variables).
  • all operations of java.concurrent.Atomic* classes

Atomic actions cannot be interleaved, so they can be used without fear of thread interference. However, this does not eliminate all need to synchronize atomic actions, because memory consistency errors are still possible.

  • isAlive()
  • join()
  • resume()
  • suspend()
  • stop()
  • start()
  • sleep()
  • destroy()

A thread is a lightweight sub process. It is an independent path of execution. A thread is executed inside a process and one process can have multiple threads.  All the threads inside a process shares a common memory area.  Since threads are independent, if an exception occurs in one thread, it doesn’t affect other threads. Threads are used to take advantage of multiple CPU cores available in a machine.

  • Thread leak is when application does not release references of the thread object and those threads do not get garbage collected.
  • Number of such unused threads increases with time and it can cause issues in the application like long response time.

To overcome this problem we can do the following
@By maintaining a log for all entry and exit point of thread.
@Check how the new thread is created and how it is closed.
@By using exception handling etc.

  • In this method of creating thread, we have to extend the Thread class and override the run() method in our class to create a Thread.
  • We have to create an object of the our class.
  • Once the object is created then we have to invoke the start() method and it will generate a new thread of execution.

For example
public class MyThread extends Thread

{
   public void run()
   {
       // code to execute under the thread
   }
   public static void main(String [] args)
   {
       MyThread c = new MyThread();
       c.start();
   }
}

Instances of ThreadLocal can be used to trport information throughout the application without the need to pass this from method to method. Examples would be the trportation of security/login information within an instance of ThreadLocal such that it is accessible by each method. Another use case would be to trport traction information or in general objects that should be accessible in all methods without passing them from method to method.

The current thread can be accessed by calling the static method currentThread() of the java.lang.Thread class. E.g. Thread.currentThread().getName().

Yes, we can call run() method of a Thread class but it will behave like a normal method and a new thread will not be created to execute the run() method. In this case the run() method will be executed in the same thread which called the run method. To actually execute it in a new Thread, we need to start it using Thread.start() method.

  • A process is a program in execution. It can also be defined as a self contained execution environment. 
  • A Thread is a single task of execution within the process. One process can have multiple threads. 
  • A process has its own memory space. 
  • A thread uses the process’s memory space and share it with the other threads inside the process.

In Java, wait and notify methods acts as synchronization utility and are essential methods for inter thread communication. Hence these methods are defined in Object class so that every object will have access to it. Also every Object has a monitor and Locks are made available on per Object basis. This is another reason why wait and notify is declared in Object class rather then Thread class.

A new thread of execution with a new call stack starts. The state of thread changes from new to runnable. When the thread gets chance to execute its target run() method starts to run.

A thread can enter the waiting state by the following ways:

  • We can invoke sleep() method of the thread.
  • An attempt to acquire the object’s lock can put the thread in waiting mode.
  • We can also invoke wait() method of the thread.
  • A thread can also be entered in waiting state by invoking its suspend() method.

The thread pool used for parallel stream operations can be accessed by ForkJoinPool.commonPool(). This way we can query its level of parallelism with commonPool.getParallelism(). The level cannot be changed at runtime but it can be configured by providing the following JVM parameter: -Djava.util.concurrent.ForkJoinPool.common.parallelism=5.

If a thread has been instantiated but not started its is said to be in new state. Unless until a start() method is invoked on the instance of the thread, it will not said to be alive. If you do not call a start() method on the newly created thread instance thread is not considered to be alive. If the start() method is not invoked and the run() method is directly called on the Thread instance, the code inside the run() method will not run in a separate new thread but it will start running in the existing thread.

Threads can communicate using wait(), notify() and notifyAll() methods. Read this post to understand inter thread communication.

synchronized keyword can be applied to static/non-static methods or a block of code. Only one thread at a time can access synchronized methods and if there are multiple threads trying to access the same method then other threads have to wait for the execution of method by one thread. Synchronized keyword provides a lock on the object and thus prevents race condition. E.g.
public void synchronized method(){} 
  public void synchronized staticmethod(){}
  public void myMethod(){
     synchronized (this){ 
        //synchronized keyword on block of code
     }
  }

The methods of Hashtable are all synchronized. This is not the case for the HashMap implementation. Hence Hashtable is thread-safe whereas HashMap is not thread-safe. For single-threaded applications it is therefore more efficient to use the “newer” HashMap implementation.

  • wait() is a method of Object class. sleep() is a method of Thread class.
  • sleep() allows the thread to go to sleep state for x milliseconds. When a thread goes into sleep state it doesn’t release the lock. wait() allows thread to release the lock and goes to suspended state. The thread is only active when a notify() or notifAll() method is called for the same object.

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing, t.join() causes the current thread(the thread which calls t.join(), mostly the main thread) to pause execution until t’s thread terminates.

  • Starvation is a situation when some threads acquired the shared resources for long time and therefore other threads are not able to access those resources and not able to do anything further.
  • For example, suppose an object provides a synchronized method that often takes a long time to return.
  • If one thread invokes this method frequently, other threads that also require frequent synchronized access to that object will be blocked.
  • In Java, Starvation can be caused by inappropriate allocation of thread priorities.
  • A thread with low priority can be starved by the threads of higher priority if the higher priority threads do not release shared resources time to time.

In general each thread has its own copy of variable, such that one thread is not concerned with the value of same variable in the other thread. But sometime this may not be the case. Consider a scenario in which the count variable is holding the number of times a method is called for a given class irrespective of any thread calling, in this case irrespective of thread access the count has to be increased so the count variable is declared as volatile.
The copy of volatile variable is stored in the main memory, so every time a thread access the variable even for reading purpose the local copy is updated each time from the main memory. The volatile variable also have performance issues.