Basic Concepts
1. What is a thread in Java?
A thread is the smallest unit of execution within a process. Java provides built-in support for multithreading through the Thread
class and Runnable
interface. Threads share the same memory space of the process that created them, allowing for efficient communication between threads.
2. What are the different ways to create a thread in Java?
There are two main ways:
- Extending Thread class: Create a class that extends
Thread
and override itsrun()
method.
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
- Implementing Runnable interface: Create a class that implements
Runnable
and pass it to aThread
object.
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running");
}
}
Thread thread = new Thread(new MyRunnable());
3. What's the difference between extending Thread vs implementing Runnable?
- Extending Thread means your class cannot extend any other class (Java doesn't support multiple inheritance)
- Implementing Runnable is more flexible as your class can still extend another class
- Implementing Runnable represents a task that can run on any thread, not just a specific thread type
- Thread class has some additional methods like yield(), interrupt() that may not be needed
4. What is the difference between process and thread?
- Process: Independent execution unit with its own memory space
- Thread: Lightweight sub-process that shares the memory space of the process that created it
Key differences:
- Processes are isolated; threads share memory
- Context switching between processes is more expensive than between threads
- Processes are more secure as one process cannot access another's memory
- Threads within a process can communicate more easily
Thread Lifecycle and States
5. What are the different states of a thread in Java?
A thread can be in one of these states:
- New: When thread is created but not yet started
- Runnable: When thread is ready to run, waiting for CPU time
- Running: When thread is currently executing
- Blocked/Waiting: When thread is waiting for a monitor lock or another thread's action
- Timed Waiting: When thread is waiting for a specified time period
- Terminated: When thread has completed execution
6. What is the difference between sleep() and wait() methods?
- sleep():
- Defined in Thread class
- Doesn't release the lock
- Used for time-based waiting
- Wakes up after specified time
- wait():
- Defined in Object class
- Releases the lock
- Used for inter-thread communication
- Needs notify()/notifyAll() to wake up
7. What is the purpose of join() method?
The join()
method allows one thread to wait for the completion of another thread. When thread A calls threadB.join()
, thread A will wait until thread B completes its execution.
Thread threadB = new Thread(() -> {
// some work
});
threadB.start();
threadB.join(); // main thread waits for threadB to complete
Thread Synchronization
8. What is thread synchronization and why is it important?
Thread synchronization is the mechanism that ensures that only one thread can access a shared resource at a time. It's important to prevent:
- Race conditions: When threads access and modify shared data simultaneously
- Data inconsistency: When threads see partial or corrupted data
- Thread interference: When operations of multiple threads overlap incorrectly
9. What are the different ways to achieve thread synchronization in Java?
- Synchronized methods: Add
synchronized
keyword to method signature - Synchronized blocks: Enclose code in
synchronized(object)
block - Volatile variables: Mark variable as
volatile
for visibility guarantees - Atomic classes: Use classes from
java.util.concurrent.atomic
package - Locks: Use
ReentrantLock
and other lock implementations
10. What is a deadlock and how can it be avoided?
Deadlock is a situation where two or more threads are blocked forever, each waiting for the other to release a lock.
Conditions for deadlock:
- Mutual exclusion
- Hold and wait
- No preemption
- Circular wait
Avoidance techniques:
- Avoid nested locks
- Lock ordering (always acquire locks in same order)
- Lock timeout (tryLock() with timeout)
- Deadlock detection and recovery
11. What is the difference between synchronized method and synchronized block?
- Synchronized method: Locks on the entire object (for instance methods) or class (for static methods)
- Synchronized block: Locks on a specific object, allowing finer-grained control
Synchronized blocks are generally preferred because:
- They reduce the scope of locking
- They can use objects other than
this
for locking - They can be more efficient by locking only critical sections
12. What is a volatile keyword in Java?
The volatile
keyword:
- Ensures visibility of changes to variables across threads
- Prevents compiler optimizations that might reorder operations
- Doesn't provide atomicity (for that you need synchronization or atomic classes)
Use cases:
- When a variable is shared among threads
- When only one thread writes and others read
- For simple flags or status variables
Advanced Concepts
13. What is the Java Memory Model (JMM)?
The Java Memory Model defines:
- How threads interact through memory
- How changes made by one thread become visible to others
- The ordering of operations in a multithreaded environment
Key concepts:
- Happens-before relationship: Guarantees memory visibility
- Memory barriers: Prevent certain types of reordering
- Atomicity, Visibility, Ordering: Key properties for thread safety
14. What is the Executor framework in Java?
The Executor framework (in java.util.concurrent
) provides a higher-level replacement for working with threads directly. It includes:
- Executor: Basic interface for executing tasks
- ExecutorService: Extends Executor with lifecycle methods
- ThreadPoolExecutor: Configurable thread pool implementation
- ScheduledExecutorService: For delayed or periodic execution
Benefits:
- Thread reuse (better performance)
- Task submission decoupled from execution
- Better resource management
15. What is the difference between Callable and Runnable?
- Runnable:
- Has a single
run()
method - Cannot return a result
- Cannot throw checked exceptions
- Has a single
- Callable:
- Has a single
call()
method - Can return a result (using Future)
- Can throw checked exceptions
- Introduced in Java 5 as part of the concurrency API
- Has a single
16. What is Future in Java concurrency?
Future
represents the result of an asynchronous computation. It provides methods to:
- Check if computation is complete (
isDone()
) - Retrieve the result (
get()
- blocks until ready) - Cancel the computation (
cancel()
)
Used with ExecutorService.submit()
which returns a Future
object.
17. What are Thread Pools and why should we use them?
Thread pools manage a pool of worker threads that execute tasks. Benefits include:
- Reduced overhead (thread creation is expensive)
- Better resource management (limits number of threads)
- Improved responsiveness (tasks can start immediately if threads available)
- Built-in task queueing
Java provides Executors
factory class with methods like:
newFixedThreadPool()
newCachedThreadPool()
newScheduledThreadPool()
newSingleThreadExecutor()
18. What is the Fork/Join framework?
The Fork/Join framework (Java 7+) is designed for work that can be broken into smaller pieces recursively. It uses:
- ForkJoinPool: Special thread pool for fork/join tasks
- RecursiveTask: For tasks that return a result
- RecursiveAction: For tasks that don't return results
Key features:
- Work-stealing algorithm (idle threads steal work from busy ones)
- Ideal for divide-and-conquer algorithms
- Efficient for highly parallelizable tasks
Concurrency Utilities
19. What are Atomic classes in Java?
Atomic classes (in java.util.concurrent.atomic
) provide:
- Atomic operations on single variables
- Lock-free thread-safe programming
- Classes like
AtomicInteger
,AtomicLong
,AtomicReference
, etc.
Benefits:
- Better performance than synchronization in many cases
- Simpler code for single variable updates
- Support for compound operations (compareAndSet)
20. What is CountDownLatch?
CountDownLatch
is a synchronization aid that allows one or more threads to wait until a set of operations completes. Key methods:
countDown()
: Decrements the countawait()
: Blocks until count reaches zero
Use cases:
- Waiting for initialization to complete
- Starting a group of threads at once
- Waiting for several services to complete
21. What is CyclicBarrier?
CyclicBarrier
allows a set of threads to wait for each other to reach a common barrier point. Unlike CountDownLatch
:
- It can be reused after the waiting threads are released
- It can take a Runnable that runs when the barrier is tripped
Use cases:
- Parallel computation where tasks are divided
- Multi-player games waiting for all players
- Simulation systems with synchronization points
22. What is Semaphore?
A Semaphore
controls access to a shared resource through permits. Key methods:
acquire()
: Gets a permit, blocks if none availablerelease()
: Releases a permit
Types:
- Counting semaphore: Allows a maximum number of threads
- Binary semaphore (like Mutex): Only one permit
Use cases:
- Resource pooling
- Throttling
- Producer-consumer problems
23. What is the difference between ConcurrentHashMap and Hashtable?
- Hashtable:
- Fully synchronized (all methods)
- Doesn't allow null keys/values
- Single lock for entire table (poor concurrency)
- ConcurrentHashMap:
- Fine-grained locking (higher concurrency)
- Allows concurrent reads and limited concurrent writes
- Uses lock stripping (locks only part of the map)
- Better performance in multi-threaded environments
24. What is the difference between BlockingQueue and non-blocking Queue?
- BlockingQueue:
- Provides blocking operations (put/take)
- Threads wait when queue is full/empty
- Implementations: ArrayBlockingQueue, LinkedBlockingQueue
- Non-blocking Queue (like ConcurrentLinkedQueue):
- Uses CAS (Compare-And-Swap) operations
- Doesn't block threads
- Better performance in high contention scenarios
Best Practices and Performance
25. What are some common multithreading issues in Java?
- Race conditions: Unpredictable behavior due to improper synchronization
- Deadlocks: Threads waiting indefinitely for each other
- Livelocks: Threads keep responding but make no progress
- Starvation: Thread doesn't get CPU time or resources
- Memory consistency errors: Threads see inconsistent views of shared data
- Thread leaks: Threads are created but never terminated
26. What are some best practices for multithreading in Java?
- Prefer higher-level concurrency utilities over raw threads
- Use thread pools instead of creating new threads for each task
- Keep synchronization to a minimum (only where needed)
- Prefer immutable objects where possible
- Document thread-safety clearly in your APIs
- Use concurrent collections instead of synchronized collections
- Consider using
final
fields for thread-safety - Be cautious with lazy initialization (use proper synchronization)
27. How can you improve performance of a multithreaded application?
- Minimize lock contention (reduce synchronized blocks)
- Use concurrent collections
- Consider lock-free algorithms where possible
- Use appropriate thread pool sizes
- Avoid excessive context switching
- Use thread-local variables where appropriate
- Balance workload among threads
- Profile and measure performance
28. What is thread-local variable in Java?
A ThreadLocal
variable provides thread-local storage where each thread has its own independently initialized copy. Use cases:
- Per-thread context (like user ID in web apps)
- Performance optimization (avoid synchronization)
- When you need to associate state with a thread
Example:
private static final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
Java 8+ Features
29. How does parallel stream work in Java 8?
Parallel streams leverage the Fork/Join framework to:
- Split the data into chunks
- Process chunks in parallel
- Combine the results
Key points:
- Underlying uses common ForkJoinPool
- Number of threads depends on available processors
- Not always faster (consider overhead)
- Use for CPU-intensive tasks with large datasets
Example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
int sum = numbers.parallelStream().mapToInt(i -> i).sum();
30. What are CompletableFuture and how do they improve upon Future?
CompletableFuture
(Java 8+) extends Future
with:
- Asynchronous computation with callbacks
- Composition and combination of async operations
- Exception handling
- Manual completion capability
Advantages over Future
:
- Non-blocking operations (thenApply, thenAccept, etc.)
- Chaining of operations
- Better exception handling
- Ability to combine multiple futures
Example:
CompletableFuture.supplyAsync(() -> fetchData())
.thenApply(data -> process(data))
.thenAccept(result -> use(result))
.exceptionally(ex -> handleError(ex));
These questions cover a wide range of multithreading concepts in Java, from basic to advanced levels, and should help you prepare for most Java multithreading interviews.