A good hands on experience is required to understand the core threads concepts. Most of the Core Java interview questions are derived from Multi-Threading & Collections framework.
This post is a collection of some good questions on Java threads, which can be asked to a senior java developer.
- A thread-local variable effectively provides a separate copy of its value for each thread that uses it.
- ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread
- In case when multiple threads access a ThreadLocal instance, separate copy of Threadlocal variable is maintained for each thread.
- Common use is seen in DAO pattern where the DAO class can be singleton but the Database connection can be maintained separately for each thread. (Per Thread Singleton)
- public class Common {
- public synchronized void synchronizedMethod1() {
- System.out.println("synchronizedMethod1 called");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("synchronizedMethod1 done");
- }
- public void method1() {
- System.out.println("Method 1 called");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("Method 1 done");
- }
- }
- public class MyThread extends Thread {
- private int id = 0;
- private Common common;
- public MyThread(String name, int no, Common object) {
- super(name);
- common = object;
- id = no;
- }
- public void run() {
- System.out.println("Running Thread" + this.getName());
- try {
- if (id == 0) {
- common.synchronizedMethod1();
- } else {
- common.method1();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) {
- Common c = new Common();
- MyThread t1 = new MyThread("MyThread-1", 0, c);
- MyThread t2 = new MyThread("MyThread-2", 1, c);
- t1.start();
- t2.start();
- }
- }
- Running ThreadMyThread-1
- synchronizedMethod1 called
- Running ThreadMyThread-2
- Method 1 called
- synchronizedMethod1 done
- Method 1 done
- public class Common {
- public synchronized void synchronizedMethod1() {
- System.out.println("synchronizedMethod1 called");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("synchronizedMethod1 done");
- }
- public synchronized void synchronizedMethod2() {
- System.out.println("synchronizedMethod2 called");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("synchronizedMethod2 done");
- }
- }
- public class MyThread extends Thread {
- private int id = 0;
- private Common common;
- public MyThread(String name, int no, Common object) {
- super(name);
- common = object;
- id = no;
- }
- public void run() {
- System.out.println("Running Thread" + this.getName());
- try {
- if (id == 0) {
- common.synchronizedMethod1();
- } else {
- common.synchronizedMethod2();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) {
- Common c = new Common();
- MyThread t1 = new MyThread("MyThread-1", 0, c);
- MyThread t2 = new MyThread("MyThread-2", 1, c);
- t1.start();
- t2.start();
- }
- }
- When two threads call Thread.join() on each other.
- When two threads use nested synchronized blocks to lock two objects and the blocks lock the same objects in different order.
LiveLock
Livelock occurs when all threads are blocked, or are otherwise unable to proceed due to unavailability of required resources, and the non-existence of any unblocked thread to make those resources available. In terms of Java API, thread livelock can occur in following conditions:- When all the threads in a program execute Object.wait(0) on an object with zero parameter. The program is live-locked and cannot proceed until one or more threads call Object.notify() or Object.notifyAll() on the relevant objects. Because all the threads are blocked, neither call can be made.
- When all the threads in a program are stuck in infinite loops.
Starvation
Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by "greedy" threads. 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 need frequent synchronized access to the same object will often be blocked. Starvation occurs when one thread cannot access the CPU because one or more other threads are monopolizing the CPU. In Java, thread starvation can be caused by setting thread priorities inappropriately. A lower-priority thread can be starved by higher-priority threads if the higher-priority threads do not yield control of the CPU from time to time.- findMonitorDeadlockedThreads() - This method can be used to detect cycles of threads that are in deadlock waiting to acquire object monitors. It returns an array of thread IDs that are deadlocked waiting on monitor.
- findDeadlockedThreads() - It returns an array of thread IDs that are deadlocked waiting on monitor or ownable synchronizers.
- Simple to construct, test, and use.
- Automatically thread-safe and have no synchronization issues.
- Step 1
On UNIX, Linux and Mac OSX Environment run below command:
ps -el | grep java
On Windows:
Press Ctrl+Shift+Esc to open the task manager and find the PID of the java process
- Step 2:
Use jstack command to print the Java stack traces for a given Java process PID
jstack [PID]
More details of jstack command can be found here : JSTACK Command Manual
- Give unique and descriptive names to the threads created in application. - Add log entry in all thread at various entry and exit points in threads.
- Change debugging config levels (debug, info, error etc) and analyze log messages.
- When you find the class that is leaking out threads check how new threads are instantiated and how they're closed.
- Make sure the thread is Guaranteed to close properly by doing following - Handling all Exceptions properly.
- Make sure the thread is Guaranteed to close properly by doing following
- Handling all Exceptions properly.
- releasing all resources (e.g. connections, files etc) before it closes.
- Using thread pools minimizes the JVM overhead due to thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and de-allocating many thread objects creates a significant memory management overhead.
- You have control over the maximum number of tasks that are being processed in parallel (= number of threads in the pool).