Multithreading is an essential concept in Java software development, allowing multiple
threads to execute concurrently, enhancing the performance of applications, especially in
environments requiring real-time or intensive computational tasks. In interviews for roles like
Java Developer, C++ Developer, Software Engineer, or even positions focused on
performance optimization, multithreading often comes up.
Here are 50 interview questions on multithreading, along with their answers, to help you
prepare:
1. What is multithreading in Java?
Multithreading in Java allows multiple threads to run concurrently within a
single program.
This enables efficient CPU usage by allowing the operating system to execute
different parts of a program at the same time.
Multithreading improves the performance of applications, especially those that
need to perform multiple tasks simultaneously, like games, real-time data
processing, or web servers.
2. What is a Thread and how does it differ from a Process?
A Process is an independent execution unit with its own dedicated memory
space (Heap, Stack, etc.) provided by the Operating System. A Thread is a
"lightweight process" that exists inside a process and shares its resources.
3. Why do we actually need Multithreading?
Multithreading is not just about "doing things at the same time"; it’s about
resource utilization:
Better CPU Utilization: While one thread is waiting for a slow I/O task (like
reading a file or a database), another thread can use the CPU for calculations.
Responsiveness: In UI applications (like Android or Desktop apps), the "Main
Thread" handles user clicks while background threads handle data loading.
This prevents the "App Not Responding" freeze.
Parallelism: On modern multi-core processors, multithreading allows tasks to
run physically at the same time on different cores, drastically reducing
execution time.
4. Why is implementing Runnable preferred over extending Thread?
There are three primary reasons:
Java Single Inheritance: Java doesn't support multiple inheritance. If you
extend Thread, you cannot extend any other class (like BaseService). By
implementing Runnable, you keep your inheritance option open.
Separation of Concerns: Runnable represents the task, while Thread represents
the worker. Decoupling the task from the runner makes the code cleaner and
easier to maintain.
Object Reusability (Thread Pools): You can pass a Runnable instance to an
ExecutorService to be executed by a pool of threads. If you extend Thread, each
task is tied to a specific thread object that cannot be reused once it finishes.
5. How do you create a thread in Java?
You can create a thread in Java by either:
Extending the Thread class: Create a subclass of Thread and override
the run() method to define the task.
Implementing the Runnable interface: Create a class that
implements Runnable and define the run() method. Then, pass an
instance of the class to a Thread object and start the thread by calling
its start() method.
6. What are the different states of a thread in Java?
The states are:
New: A thread is in this state when it is created but not yet started.
Runnable: A thread is in this state when it is ready to execute, but the
JVM scheduler decides when to run it.
Blocked: A thread enters this state when it is waiting for a lock to be
released by another thread.
Waiting: A thread enters this state when it is waiting indefinitely for
another thread to perform a particular action (e.g., wait() method).
Timed Waiting: A thread enters this state when it is waiting for a
specific amount of time (e.g., sleep(), join() with a timeout).
Terminated: A thread enters this state when it has finished execution
or was terminated due to an error.
7. Difference between Runnable and Thread in Java?
Runnable is an interface that defines a single method run(), which contains
the code to be executed by a thread.
On the other hand, Thread is a class that represents an actual thread of
execution.
A Thread can implement the Runnable interface, allowing you to decouple the
task from the thread management, enabling better code reuse.
8. What happens when you call run() vs start()?
start(): Creates a new thread and puts it in the Runnable state. The JVM then calls
the run() method on this new thread.
run(): No new thread is created. The code inside the run() method executes on the
current thread (just like a normal method call).
9. Purpose of the start() method in the Thread class?
The start() method is used to initiate the execution of a thread.
Calling start() causes the thread to transition from the New state to
the Runnable state. The actual execution of the thread happens when the
JVM scheduler selects it to run. The start() method internally calls
the run() method, where the thread’s task is defined.
10. What is synchronization in Java?
Synchronization in Java is a mechanism that ensures that multiple threads do
not access shared resources concurrently, which could lead to inconsistent
states or race conditions. By using synchronization, only one thread can
access a resource at a time, maintaining thread safety. This is especially
important when multiple threads are modifying or reading shared data.
11. How does synchronized keyword work in Java?
The synchronized keyword can be used to lock a method or a block of code,
ensuring that only one thread can execute it at any given time. When a thread
enters a synchronized method or block, it acquires the associated lock. Other
threads that try to enter the same synchronized method or block must wait
until the lock is released.
12. What is a deadlock?
A deadlock is a situation where two or more threads are blocked forever, each
waiting for a resource that the other holds. This can occur when threads are
locked in a circular dependency, where thread A holds a lock needed by
thread B, and thread B holds a lock needed by thread A. This prevents the
threads from making progress.
13. How to resolve deadlock or prevent it?
To prevent deadlock, you can use these strategies:
Lock Ordering: Always acquire locks in a consistent, predefined global order.
Lock Timeout: Use tryLock(long time, TimeUnit unit) from the ReentrantLock
class instead of synchronized blocks to avoid waiting indefinitely.
Deadlock Detection: Use tools like JConsole or Thread Dumps (jstack) to
identify and then break the cycle.
14. Class level Synchronization (static vs non-static)
Static Synchronization: Locks the Class object (e.g., [Link]). All instances
of that class share this single lock.
Non-static Synchronization: Locks the specific instance (this). Different instances
have different locks and can execute the method concurrently.
15. Different ways to achieve thread synchronization in Java?
Thread synchronization can be achieved using:
Synchronized Methods: Locking an entire method so that only one
thread can execute it at a time.
Synchronized Blocks: Locking a specific block of code within a
method, providing more granular control over which sections of code
are synchronized.
Explicit Locks: Using ReentrantLock and other locks from
the [Link] package for more advanced
synchronization features, such as trying to acquire a lock without
blocking.
16. Difference between synchronized method and synchronized block?
A synchronized method locks the entire method, ensuring that only one
thread can execute that method at a time. This can potentially lock
unnecessary parts of the code.
A synchronized block locks only the specific block of code within a method,
providing more flexibility and allowing you to lock just the critical section that
requires synchronization, reducing the scope of contention.
17. How do threads communicate with each other?
Threads communicate using wait(), notify(), and notifyAll() methods, which
are part of the Object class in Java. These methods are typically used for
inter-thread communication in situations where threads need to coordinate
their execution based on some shared state or condition.
18. Purpose of wait(), notify(), and notifyAll()?
wait() causes the current thread to release the lock and enter a waiting state
until another thread calls notify() or notifyAll() on the same object.
notify() wakes up a single thread that is waiting on the object's monitor.
notifyAll() wakes up all threads that are waiting on the object's monitor,
allowing them to compete for the lock.
19. Why are wait(), notify(), and notifyAll() in Object and not in Thread?
In Java, every object has an intrinsic lock (monitor). Since waiting and notifying are
operations based on the ownership of a lock, and locks are associated with objects,
these methods belong to the Object class. This allows any object to act as a
synchronization primitive.
20. What is "intrinsic lock"?
An intrinsic lock (or monitor) is an implicit internal entity associated with every object
instance. When a thread calls a synchronized method, it automatically acquires the
intrinsic lock for that object and releases it when the method returns or throws an
exception.
21. What is ThreadLocalMap? How is it working?
ThreadLocalMap is a customized hash map used strictly to maintain thread-local
values.
How it works: Each Thread object maintains its own internal ThreadLocalMap. The
ThreadLocal instance acts as the key. When you call get() or set(), the thread looks
into its own map, ensuring data isolation from other threads.
22. Java memory management (volatile keyword) / Difference between synchronized &
volatile & Atomic
volatile: Ensures visibility. Changes made by one thread to a variable are
immediately visible to others. It prevents instruction reordering but does not provide
atomicity (e.g., count++ is not safe).
synchronized: Ensures visibility and atomicity. Only one thread can enter the block
at a time. It is a "heavyweight" locking mechanism.
Atomic classes: (e.g., AtomicInteger) Use low-level Compare-And-Swap (CAS)
operations. They provide atomicity and visibility without the overhead of traditional
locking (lock-free).
23. sleep(), wait(), join()
sleep(): A static method of Thread. The thread stops for a period but does not
release its locks.
wait(): An Object method. The thread releases the lock and waits until another thread
calls notify()/notifyAll() on the same object.
join(): Used to pause the current thread until the thread it was called on completes
its execution.
24. What happens when you call start() two times?
It throws an IllegalThreadStateException. Once a thread has started or completed
execution, it cannot be restarted.
25. CountDownLatch, CyclicBarrier, Semaphore
CountDownLatch: Allows one or more threads to wait until a set of operations being
performed in other threads completes. It cannot be reset.
CyclicBarrier: Allows a set of threads to all wait for each other to reach a common
barrier point. It can be reused (reset).
Semaphore: Maintains a set of permits. Threads "acquire" permits to access a
resource and "release" them when done. Used for throttling (e.g., limiting DB
connections).
26. Blocking queue in Java?
A blocking queue is a type of queue that supports operations where threads
wait for space to become available when adding elements to the queue and
wait for elements to become available when retrieving them. It is used in
producer-consumer scenarios, where the producer waits if the queue is full
and the consumer waits if the queue is empty.
27. What is a Condition in Java concurrency?
A Condition provides a more advanced way to coordinate the execution of
threads.
It allows a thread to wait for a specific condition to be met before continuing. It
is often used in conjunction with ReentrantLock and provides better control
over thread communication compared to the wait() and notify() methods.
28. What is thread safety and why is it important?
Thread safety is the property of an object or method that ensures it functions
correctly when accessed concurrently by multiple threads. It is important
because without thread safety, threads may interfere with each other, leading
to inconsistent states, data corruption, or application crashes. Ensuring thread
safety prevents these issues and allows multiple threads to interact with
shared data safely.
29. What are the ways to achieve thread safety in Java?
Thread safety can be achieved using:
Synchronization: Using synchronized blocks or methods to control
access to shared resources.
Volatile Variables: Ensuring that changes to variables are
immediately visible to other threads.
Atomic Variables: Using classes
like AtomicInteger or AtomicReference to perform operations
atomically.
Concurrent Collections: Using thread-safe collections
like ConcurrentHashMap or CopyOnWriteArrayList for managing
shared data.
30. What is an atomic operation?
An atomic operation is a series of operations that are executed as a single,
indivisible unit of work. It ensures that no other thread can interfere with the
operation. For example, incrementing a variable atomically means that no
other thread can change the value of the variable between the read and write
operations.
31. Classes in the [Link] package?
The [Link] package provides several classes designed
for atomic operations on single variables:
AtomicInteger
AtomicLong
AtomicBoolean
AtomicReference These classes allow safe updates to variables in
multithreaded environments without the need for explicit
synchronization.
32. What is the difference between volatile keyword and Atomic classes?
The volatile keyword ensures visibility of changes to a variable across all
threads, meaning when one thread modifies a variable, other threads see the
updated value immediately. However, it does not ensure atomicity (i.e., it
doesn't prevent race conditions).
Atomic classes, such as AtomicInteger, provide both visibility and atomicity for
operations like incrementing or updating a variable.
33. What is the ExecutorService in Java and how is it different from using threads
directly?
ExecutorService is an interface in Java that simplifies thread management. It
provides methods to submit tasks for execution, manage thread pools, and
shut down threads gracefully. Unlike using threads
directly, ExecutorService abstracts away low-level thread management and
provides better control over task scheduling and execution.
34. How do you create an ExecutorService?
You can create an ExecutorService using the factory methods provided by
the Executors class:
[Link](int nThreads) creates a fixed-size
thread pool.
[Link]() creates a thread pool that can
expand as needed.
[Link]() creates a single-threaded
executor.
[Link](int corePoolSize) creates a pool
that supports scheduled tasks.
35. Difference between execute() and submit() methods in ExecutorService.
execute(): Used for submitting tasks that do not return any result or need to
be tracked (i.e., void tasks). It does not return any value.
submit(): Used for tasks that return a result. It returns a Future object, which
can be used to retrieve the result of the task or check if it has completed.
36. How do you gracefully shut down an ExecutorService?
You can gracefully shut down an ExecutorService by calling
the shutdown() method. This initiates an orderly shutdown in which previously
submitted tasks are executed, but no new tasks will be accepted. You can
also use shutdownNow() to attempt to stop all actively executing tasks and
halt the processing of waiting tasks.
37. Difference between shutdown() and shutdownNow() methods in
ExecutorService?
shutdown(): Initiates an orderly shutdown, where tasks that were already
submitted are completed before the service is fully shut down. No new tasks
will be accepted.
shutdownNow(): Tries to stop all actively executing tasks and attempts to stop
any waiting tasks. It returns a list of the tasks that were waiting to be
executed.
38. What is a Future in Java and how is it related to ExecutorService?
A Future represents the result of an asynchronous computation. When you
submit a task to an ExecutorService using submit(), it returns a Future object.
You can use the Future object to check if the task is completed, retrieve the
result, or cancel the task.
39. How can you cancel a task that has been submitted to an ExecutorService?
You can cancel a task by calling the cancel() method on the Future object
returned by the submit() method. If the task is still running, calling cancel() will
attempt to interrupt it. If the task has already finished or been canceled, it will
have no effect.
40. What is a ScheduledExecutorService and how do you use it?
A ScheduledExecutorService is a type of ExecutorService that can schedule
commands to run after a given delay or to execute periodically. It is useful for
tasks that need to be repeated at fixed intervals or scheduled to run after a
delay.
You can use methods like schedule() and scheduleAtFixedRate() to schedule
tasks.
41. How do you handle exceptions thrown by tasks submitted to an
ExecutorService?
Exceptions thrown by tasks can be handled by catching them within
the run() method of the task. If you are using submit(), you can handle
exceptions by calling [Link](). If the task throws an exception, get() will
throw an ExecutionException, which can be caught and processed.
42. Explain the lifecycle of an ExecutorService?
The lifecycle of an ExecutorService involves:
Creation: Instantiate an ExecutorService using one of the factory
methods.
Task Submission: Submit tasks for execution
using execute() or submit().
Execution: The service manages the execution of tasks using a
thread pool.
Shutdown: The service is shut down gracefully
using shutdown() or shutdownNow().
43. What is a thread pool and why is it used?
A thread pool is a collection of pre-instantiated, reusable threads that are
used to execute tasks. Thread pools are used to manage the execution of
concurrent tasks efficiently, reducing the overhead of creating and destroying
threads for each task. They improve performance by reusing threads and
controlling the number of concurrent threads.
44. Different types of thread pools provided by the Executors utility class?
The Executors utility class provides several types of thread pools:
Fixed Thread Pool: A pool with a fixed number of threads
(newFixedThreadPool(int nThreads)).
Cached Thread Pool: A pool that creates new threads as needed but
reuses previously constructed threads (newCachedThreadPool()).
Single Thread Executor: A pool with a single thread for executing
tasks (newSingleThreadExecutor()).
Scheduled Thread Pool: A pool that supports scheduling tasks with a
fixed rate or delay (newScheduledThreadPool(int corePoolSize)).
45. How do you create a fixed thread pool in Java?
You can create a fixed thread pool by using the
method [Link](int nThreads), where nThreads is the
number of threads in the pool. This creates a pool with a fixed number of
threads, and if all threads are busy, new tasks will wait until a thread becomes
available.
46. How do you create a cached thread pool in Java?
You can create a cached thread pool by
using [Link](). This creates a pool that can
dynamically adjust the number of threads, creating new ones as needed, and
reusing previously idle threads when available.
47. What is a single-thread executor?
A single-thread executor is an ExecutorService that uses a single worker
thread to execute tasks. Tasks are executed sequentially in the order they are
submitted, ensuring that only one task is running at any time.
48. How do you create a scheduled thread pool?
You can create a scheduled thread pool by
using [Link](int corePoolSize). This creates a
pool that can schedule tasks to run at fixed intervals or after a delay.
49. What are the benefits of using a thread pool?
Benefits of using a thread pool include:
Efficient Resource Management: Reduces the overhead of creating
and destroying threads for each task.
Improved Performance: Reuses threads, reducing the cost of thread
creation.
Better Scalability: Controls the number of concurrent threads and
balances system resources.
Task Queueing: Allows for efficient management of tasks in a queue,
ensuring that they are executed in order.
50. How does the thread pool manage the number of threads in the pool?
The thread pool manages the number of threads using the core pool
size and maximum pool size. It creates new threads if the number of active
threads is less than the core size, and if the number of active threads
exceeds the core size, it creates additional threads up to the maximum pool
size. Idle threads are terminated if they exceed the keep-alive time.
51. Difference between a fixed thread pool and a cached thread pool?
A fixed thread pool has a fixed number of threads, and the size of the pool is
determined at the time of creation. Once the number of threads is reached,
new tasks are queued until a thread becomes available.
A cached thread pool creates new threads as needed and reuses threads
that are no longer active. If there are no available threads, new ones are
created dynamically, and idle threads are terminated after a certain period of
inactivity.
52. How does ThreadPoolExecutor work internally?
ThreadPoolExecutor internally maintains a pool of worker threads, which
execute tasks submitted to it. It uses a blocking queue to hold tasks that are
waiting for execution. The executor uses a core pool size to manage the
number of threads, and can create additional threads up to the maximum pool
size if necessary.
53. What are the core and maximum pool sizes in ThreadPoolExecutor?
The core pool size is the minimum number of threads that are maintained in
the pool, even if they are idle.
The maximum pool size is the maximum number of threads that the pool
can have. If the number of active threads exceeds the core pool size, new
threads can be created, but they cannot exceed the maximum pool size.
54. What is the keep-alive time in ThreadPoolExecutor and how does it affect
thread pool behavior?
The keep-alive time is the amount of time that an idle thread will remain alive
before being terminated. If the number of threads in the pool exceeds the
core pool size and the threads remain idle for the specified keep-alive time,
they are terminated. This helps in managing resources by reducing
unnecessary idle threads.
55. Blocking queue and how is it used in ThreadPoolExecutor?
A blocking queue is used by ThreadPoolExecutor to hold tasks before they
are executed. It ensures that tasks are executed in the order they are
received and that threads are not overwhelmed by excessive task
submissions. Examples of blocking queues
include ArrayBlockingQueue, LinkedBlockingQueue,
and SynchronousQueue.
56. Different types of blocking queues you can use with ThreadPoolExecutor?
Some common types of blocking queues include:
ArrayBlockingQueue: A bounded blocking queue backed by an
array.
LinkedBlockingQueue: A blocking queue backed by a linked node
structure, with optional capacity limits.
SynchronousQueue: A queue that does not hold any elements; tasks
must be transferred directly between threads.
PriorityBlockingQueue: A queue that orders elements based on their
priority.
57. How would you handle a scenario where you need to perform multiple tasks in
parallel?
You can handle this by using a thread pool (e.g., ExecutorService) to submit
multiple tasks concurrently. Each task will be executed by a separate thread
from the pool, allowing tasks to run in parallel.
58. Producer-consumer problem and how can you solve it in Java?
The producer-consumer problem involves two types of threads:
The producer, which generates data and puts it into a shared queue.
The consumer, which retrieves and processes the data from the
queue.
This can be solved using a blocking queue, such
as ArrayBlockingQueue or LinkedBlockingQueue. The producer thread can
add items to the queue, and the consumer thread can remove items. The
blocking queue handles synchronization, so the consumer waits if the queue
is empty and the producer waits if the queue is full.
Another approach involves using synchronized methods or explicit
locks to control access to shared resources between the producer and
consumer.
59. Implement a singleton class in a multithreaded environment.
A singleton class ensures that only one instance of the class is created, even
in a multithreaded environment. In Java, this can be implemented using:
1. Double-Checked Locking: This method uses synchronization with an
additional check to ensure that the instance is created only once.
2. Bill Pugh Singleton Design: Using the enum type to ensure thread-safety
and serialization.
60. How do you handle exceptions in threads in Java?
Exceptions thrown within a thread can be handled using a try-catch block inside
the run() method of the Runnable or Callable interface. If a thread encounters an
exception, the exception is caught, and the thread can terminate gracefully.
Additionally, you can implement an UncaughtExceptionHandler for handling
uncaught exceptions. This handler can be set for a thread to handle any
exception that is not caught within the thread.
61. Daemon thread in Java.
A daemon thread is a thread that runs in the background and performs tasks
such as garbage collection or other housekeeping operations. It does not prevent
the JVM from exiting when all non-daemon threads have completed their
execution.
Daemon threads are typically low-priority threads. They are automatically
terminated when the JVM shuts down, so they should not be used for tasks that
need to complete reliably.
To create a daemon thread, you can call the setDaemon(true) method before
starting the thread:
62. How do you create a daemon thread in Java?
To create a daemon thread in Java, you need to call the setDaemon(true) method
on a Thread object before starting the thread. This marks the thread as a daemon
thread, which will allow it to run in the background and be terminated
automatically when the JVM exits.
63. Benefits of using the ConcurrentHashMap?
ConcurrentHashMap is a thread-safe version of HashMap that allows multiple
threads to read and write concurrently without locking the entire map.
The key benefits of using ConcurrentHashMap include:
1. High Concurrency: Allows concurrent access by multiple threads with
minimal contention. It achieves this by partitioning the map into segments,
allowing threads to work on different segments simultaneously.
2. Thread-Safety: Ensures that operations like put(), get(), and remove() are
thread-safe without the need for external synchronization.
3. Better Performance: Compared to synchronized HashMap or Hashtable,
it provides better performance in concurrent environments.
4. Non-blocking Reads: Threads can read data without locking the entire
map, thus improving performance for read-heavy operations.
Example of using ConcurrentHashMap:
64. If a Process has 4GB RAM, how is memory distributed among 10 threads?
Memory is divided into Shared and Private sections:
Shared Sections (The 4GB "Pool"):
1. Heap Memory: This is where all objects live. Every thread can
access any object in the heap if it has a reference to it.
2. Method Area (Metaspace): Stores class structures, static variables,
and constants.
Private Sections (Per-Thread):
1. Thread Stack: Each thread gets its own stack (usually 1MB by
default). This stores local variables and method call frames.
2. PC Register: Keeps track of the current instruction being executed by
the thread.
3. Native Stack: Used for native (C/C++) code execution.
Note: If you have 10 threads, you will have 10 separate Stacks (~10MB total),
but they all share the same 4GB Heap.
65. If one thread throws a StackOverflowError, does the JVM terminate the
process?
No. In Java, a StackOverflowError (or an OutOfMemoryError) is technically an
Error. When an error or exception is thrown and not caught:
The specific thread that encountered the error will terminate.
The JVM will print the stack trace to [Link].
The rest of the application (other threads) will continue to run.
The only exceptions are:
If the thread that crashed is the Main Thread and no other non-daemon
threads are running.
If the StackOverflowError happens in a critical system thread or causes a
"Fatal Error" that corrupts the JVM's internal state (very rare).
66. Why is context switching between two threads within the same process faster
than switching between two different processes?
Switching between threads is faster because they share the same Virtual Memory
space
Process Switch: The OS must flush the TLB (Translation Lookaside Buffer),
switch memory maps, and load a new process context. This is "heavy."
Thread Switch: The OS keeps the memory map but only switches the Stack
Pointer, Program Counter, and Registers. This is "light."
67. If two processes need to talk to each other, they use Sockets or Shared
Memory. How do two Java threads talk to each other?
While processes need Sockets or Pipes, Java threads talk by sharing references to
the same objects in the Heap.
Visibility: Guaranteed by the volatile keyword.
Coordination: Handled via wait(), notify(), or BlockingQueue.
68. On a quad-core machine, if you have a single-threaded application consuming
25% CPU, how does adding threads help? Is there a point where adding more
threads actually slows down the application?
On a quad-core machine, 25% CPU usage usually means one core is at 100% while
three are idle.
Adding Threads: Allows the task to utilize the other 3 cores, potentially reaching
100% total CPU usage.
The Turning Point: Adding too many threads causes Context Switch Overhead.
The CPU spends more time switching between threads than actually executing code,
leading to a performance drop (Thrashing).
69. Why is multithreading highly effective for I/O-bound tasks (like database calls)
but potentially detrimental for pure CPU-bound tasks (like heavy mathematical
computations)?
I/O-Bound: Threads spend most of their time waiting (for DB, Disk, or Network).
Multithreading is great because while Thread A waits for data, Thread B can use the
CPU.
CPU-Bound: The task is pure calculation. If you have 4 cores and run 4 threads,
it's efficient. If you run 400 threads, they fight for the same 4 cores, and the constant
switching slows everything down.
70. In a UI or Web Server context, how does multithreading prevent the
"Application Not Responding" (ANR) or "Socket Timeout" issues?
UI Context: By moving long-running tasks (like a 5-second API call) to a
background thread, the Main/UI Thread remains free to process user clicks and
redraw the screen, preventing the "Application Not Responding" (ANR) dialog.
Web Server: A thread pool (like Tomcat's) allows the server to accept a new
connection on one thread while another thread is still processing a heavy request.
71. Does creating a new Thread() object immediately create a thread in the
Operating System?
No, creating the object does not create an OS thread. It is just a plain Java object in
the Heap. The native OS thread is only spawned when you call .start()
72. How has the introduction of Functional Interfaces and Lambda expressions
changed the way we implement the Runnable interface?
Before Java 8, we used "Anonymous Inner Classes." Now, since Runnable is a
Functional Interface (has only one abstract method), we use clean Lambdas:
73. What happens if you call start() on a thread that is already in the TERMINATED
state? Why is it impossible to restart a thread?
If you call .start() on a TERMINATED thread, it throws an
IllegalThreadStateException. A thread is a "one-time-use" object. This is because the
underlying OS resources are cleaned up once the thread finishes.
74. You are using a Thread Pool. One of the threads throws an unhandled
RuntimeException. Does the Thread Pool create a new thread to replace it, or
is that thread slot lost forever?
In a standard ThreadPoolExecutor, if a thread dies due to an uncaught exception, the
pool discards that thread and creates a new one to maintain the "Core Pool Size."
75. If a thread calls [Link](), is it guaranteed that another thread will get a
chance to run immediately?
No guarantee. yield() is merely a hint to the OS scheduler. The scheduler is free to
ignore it and give the CPU immediately back to the same thread.
76. What is the "False Sharing" problem and how does Java 8+ handle it?
It happens when two different threads modify two different variables that happen to
reside on the same CPU Cache Line. Even though the variables are different, the
CPU must refresh the entire cache line, killing performance. Java 8 introduced the
@Contended annotation to add padding between variables to ensure they stay on
separate cache lines.
77. What is a "Thread Local" and when should you avoid it?
ThreadLocal allows you to store data that is accessible only by a specific thread.
Use Case: Storing a database connection or a User ID for a web request.
Danger: If used in a Thread Pool, the ThreadLocal variable might stay alive after
the request is finished because the thread is reused, not destroyed. This leads to
Memory Leaks. You must always call .remove().
78. What is the difference between "Livelock" and "Deadlock"?
Deadlock: Threads are stuck waiting for each other (stationary).
Livelock: Threads are not blocked, but they keep responding to each other's
actions in a way that prevents progress (like two people in a hallway trying to step
aside but moving in the same direction repeatedly).
79. How does ReentrantLock differ from synchronized?
Fairness: ReentrantLock can be set to "fair" (first-come, first-served).
Interruptibility: You can interrupt a thread waiting for a ReentrantLock.
TryLock: You can attempt to get a lock without blocking forever (tryLock()).