Thread creation, synchronization, deadlock, volatile, locks, and the Executor framework.
Question 1: What is the difference between a process and a thread?
Ans
A process is an independent, isolated unit of execution with its own memory space managed by the operating system. A thread is a lighter-weight unit of execution that exists inside a process, and multiple threads within the same process share that process's memory, which makes communication between them easy but also introduces the need for careful synchronization.
Example
text
Process -> Thread 1, Thread 2, Thread 3 (share the same heap memory)
Important Point
Because threads share memory, a bug in one thread, like corrupting shared data, can affect the whole process, unlike a crash in a separate process.
Question 2: What is the difference between Thread.sleep() and Object.wait()?
Ans
Thread.sleep() pauses the current thread for a fixed amount of time without releasing any locks it holds, and doesn't require any special context to call. wait() is called on an object's monitor from within synchronized code, releases that monitor's lock while waiting, and stays paused until another thread calls notify() or notifyAll() on the same object, or a timeout elapses.
Example
java
Thread.sleep(1000); // pauses this thread, keeps any locks held
synchronized (lockObj) {
lockObj.wait(); // releases lockObj's monitor while waiting
}
Important Point
wait() must be called while holding the object's monitor, inside a synchronized block on that object, or it throws IllegalMonitorStateException.
Question 3: What is the difference between a synchronized method and a synchronized block?
Ans
A synchronized method locks the entire method body using the object's own monitor, or the class's monitor for a static method, for its whole duration. A synchronized block lets you lock only the specific section of code that actually touches shared state, and optionally lock on a different object than this, which can reduce contention by keeping the locked region as small as possible.
Example
java
synchronized void increment() { count++; } // whole method locked
void incrementBlock() {
synchronized (lockObj) { count++; } // only this line locked
}
Important Point
Smaller synchronized blocks generally improve concurrency, since they hold the lock for less time, but only if you're careful not to leave any shared-state access outside the block.
Question 4: What is deadlock, and how can it be avoided?
Ans
Deadlock happens when two or more threads each hold a lock the other needs and wait for each other indefinitely, so none of them can ever proceed, for example, thread A holds lock 1 and waits for lock 2, while thread B holds lock 2 and waits for lock 1. It's commonly avoided by always acquiring multiple locks in the same, consistent order across all threads, or by using timed lock attempts that can back off instead of waiting forever.
Example
text
Thread A: lock(1) -> waits for lock(2)
Thread B: lock(2) -> waits for lock(1) // deadlock
Important Point
A related but different problem is starvation, where a thread technically can eventually proceed but keeps getting repeatedly passed over, often because other threads keep getting priority or the lock.
Question 5: What does the volatile keyword do, and what does it NOT guarantee?
Ans
volatile tells the JVM that reads and writes of that variable must always go to and from main memory, so a value written by one thread becomes immediately visible to other threads reading it, avoiding stale cached copies. What it does NOT guarantee is atomicity for compound operations — a volatile int being incremented with count++ can still lose updates under concurrent access, since read-modify-write still isn't a single atomic step.
Example
java
private volatile boolean running = true; // a simple flag one thread sets, another reads
Important Point
volatile is good for simple flags or single-write/multi-read state, not for counters or anything requiring compound read-modify-write safety.
Question 6: What is ExecutorService, and why is it preferred over manually creating threads?
Ans
ExecutorService manages a pool of reusable worker threads and lets you submit tasks to run without creating and destroying a new Thread object for every single task, which reduces the overhead of thread creation and gives you control over how many threads run concurrently.
Example
java
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("Task"));
pool.shutdown(); // important — an un-shutdown pool can keep the JVM alive
Important Point
Always plan for shutdown/lifecycle handling — a thread pool that's never shut down can prevent the application from exiting.
Question 7: What is the difference between Runnable and Callable, and what is a Future?
Ans
Runnable's run() method returns no result and can't throw a checked exception. Callable's call() method can return a value and can throw a checked exception, which is why it's used with ExecutorService.submit() when you need a result back. That submitted Callable immediately returns a Future, a placeholder for a result that may not be ready yet — calling future.get() blocks until the task completes and returns its value.
Example
java
Callable<Integer> task = () -> 10 + 20;
Future<Integer> f = executor.submit(task);
int result = f.get(); // blocks until the task finishes
Important Point
future.get() can throw ExecutionException if the task itself threw an exception — the original exception is wrapped as the cause.
Question 8: What is a thread?
Ans
A thread is an independent path of execution within a process. Java allows multiple threads to execute tasks concurrently.
Threads are useful for background work, parallelizable tasks, I/O waiting, and responsive applications.
Example
java
Thread t = new Thread(() -> System.out.println("Running"));
t.start();
Important Point
Call `start()` to create a new execution path; calling `run()` directly is just a normal method call.