Loading...
Loading...
Java interview guide
Threads, synchronization, locks, executors and concurrent collections. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
60 questionsLast updated
For two reasons. While one thread waits for a file, the network or the user, other threads can keep working, so the program stays responsive. And with several cores, several threads can calculate at the same instant.
Lesson: What is a thread?The JVM creates it when the program starts and calls main on it. Every method main calls runs on the same thread unless the code explicitly starts a new one. Its name is main, which is why crash messages say Exception in thread "main".
Extend Thread and override run(), or implement Runnable and pass the object to new Thread(task). Since Java 8 the Runnable is often a lambda or a method reference. In every case you call start() to begin the new thread; later modules add thread pools, which also take Runnable tasks.
The constructor takes a Runnable, and Runnable is a functional interface with one abstract method, void run(). A lambda with no parameters and no return value matches it, so Java turns the lambda into a Runnable.
The lambda uses the loop counter i, which changes on every pass. Copy it into a new local variable inside the loop, such as int idx = i;, and use idx in the lambda; idx never changes, so it is effectively final.
No. A second start() on the same Thread object throws IllegalThreadStateException, even if the first run has finished. To run the job again, create a new Thread and pass it the same Runnable.
Yes. getName() returns the name of the object, while Thread.currentThread() returns the thread actually executing. If someone calls run() directly from main, getName() still says, for example, printer, but the current thread is main.
No. Starting thread A before thread B only means A was created first. The scheduler may run B's first statement before A's. If the order matters, you must coordinate the threads, for example with join().
Not necessarily. Many interleavings are possible and a few test runs see only some of them; a different computer, more load or a debugger can change the timing. Correctness must come from coordination such as join(), not from what you happened to see.
Thread.sleep(ms) pauses the current thread for a fixed time, whatever other threads are doing. t.join() pauses the current thread until thread t has finished, however long that takes. Use join when you are waiting for another thread's work, never a guessed sleep.
You cannot tell from join alone, because it returns void both when the thread ends and when the 1000 ms run out. Call isAlive() right after: false means it finished, true means the wait timed out.
Thread.currentThread().join() waits for the current thread to finish, which can never happen while it is waiting, so it waits forever (unless it is interrupted). It is a bug, not a feature.
It sets the target thread's interrupt flag. If the thread is blocked in sleep, join or wait, that call wakes up and throws InterruptedException, clearing the flag. Otherwise nothing happens until the thread's code checks the flag.
Almost never. It silently cancels someone's stop request, which leads to threads that refuse to shut down. Either declare throws InterruptedException or restore the flag and stop the work.
Thread.State has six: NEW (not started), RUNNABLE (running or ready), BLOCKED (waiting for a monitor lock), WAITING (waiting with no time limit, like join()), TIMED_WAITING (waiting with a limit, like sleep(ms)) and TERMINATED (finished). A thread moves from NEW to TERMINATED and never goes back.
A background thread that does not keep the JVM alive. When the last non-daemon thread ends, the JVM exits and any daemon threads are stopped abruptly, without running their finally blocks. Garbage-collection helpers are typical daemons.
No. Priorities from 1 to 10 are only hints, and the operating system may ignore them. Correct programs never depend on priorities; they use coordination such as join(), locks or queues.
A callback with the thread and the exception as parameters, which the JVM calls when an exception escapes run(). You set it per thread with setUncaughtExceptionHandler or for all threads with Thread.setDefaultUncaughtExceptionHandler. It runs on the dying thread and is good for logging and alerts.
Have each task record its outcome, the result or the exception, and have main check every task after join(). Then the program can report "2 of 3 uploads failed" instead of quietly showing a partial total. Executors later give the same idea through Future.get().
Give each task its own result slot or object, start all threads in one loop, join all of them in a second loop, and only then combine the results. Starting and joining in the same loop would make them run one after another.
Lesson: Thread basics: common mistakes and practiceCatch exceptions inside the task and record them with the result, then check every task after join(). As a safety net, set an UncaughtExceptionHandler, at least a default one, that logs failures.
Each method call gets its own copy of its local variables on the calling thread's stack, so no other thread can read or change them. Java also stops a lambda from changing a local variable of the enclosing method. The safety covers the variable itself, not the object it refers to.
Lesson: Two threads, one variableYes, by not sharing the data while the threads work. Each thread counts or collects into its own local variable or its own slot, and main combines the results after join(). Data that nobody changes after the threads start is also safe to read from many threads.
count = count + 1 or ++count any safer than count++?No. All three compile to the same read, add and write on the field. Prefix and postfix only change which value the expression returns, not how the field is updated.
Lesson: Why count++ is not safeNot necessarily. A race shows up only with bad timing, and small or short tests may never produce it. I would still check the code for shared read-modify-write without coordination, and run tests with many iterations on a multi-core machine.
Lesson: Why count++ is not safeIt is when a thread checks a condition and then acts on it, but another thread changes the data between the check and the act. A classic example is booking: two threads both see seatsLeft > 0, both decrement, and one seat is sold twice.
if (instance == null) instance = new X(); not thread-safe?It is check-then-act. Two threads can both see null before either assigns instance, so two objects get created and different threads may use different instances. There are also visibility problems with publishing the object, which the safe singleton patterns handle.
The gap between the check and the act is usually tiny, so two threads must hit it at almost the same moment. Light test traffic rarely does that, while a real rush, like Tatkal booking at 10 am, does it often.
Lesson: Check-then-act bugsNo. All synchronized instance methods of an object use that one object's lock, so a second thread waits even if it calls a different method. On two different objects, they can run at the same time, because each object has its own lock.
Lesson: synchronized methodsNo. javac rejects it with "modifier synchronized not allowed here". It is not needed, because while the constructor runs, only the creating thread has a reference to the new object, unless the constructor leaks this to another thread, which you should never do.
this?Any code with a reference to my object can lock this, and if it holds that lock for long, my methods have to wait, or it can even take part in a deadlock. A private lock can only be taken by my own class, and final guarantees every thread always locks the same object.
Reentrancy means a thread that holds a lock can take it again without blocking; the JVM counts the holds and frees the lock when the count returns to zero. Without it, a synchronized method calling another synchronized method on the same object, or calling super, would wait forever for itself.
While one thread holds a lock, every other thread that needs it waits, so the locked code effectively runs on one thread at a time. Slow work inside the lock, such as I/O or network calls, turns into a queue and kills the benefit of having many threads. I keep only the steps that must be atomic inside the block.
Lesson: Keep locked sections smallYes. If a check is in one synchronized block and the action in another, the lock is released in between and another thread can run there. For example, checking for a free seat in one block and decrementing in another lets two threads both book the last seat, even though every access is synchronized.
Lesson: Keep locked sections smallIt behaves correctly when many threads call its methods at the same time, in any interleaving, without the callers adding their own locks. In practice its invariants always hold and every method gives a result that matches some order of the calls.
Lesson: Design a thread-safe classIt can leak its state, for example a getter that returns the internal ArrayList, which callers then use with no lock. Or its API can force callers into compound actions, like if (acc.getBalance() >= x) acc.setBalance(...), which is not atomic across two calls.
I put a comment like // guarded by lock next to each field, often with the invariant. Some teams use a @GuardedBy("lock") annotation from a library, which some static analysis tools can check. Either way, the next developer knows which lock every new method must take.
isFree() and then reserve(), if both are synchronized?The lock is released between the two calls, so another thread can reserve the seat in between, and both callers think they got it. The class should offer one method, like tryReserve(), that checks and reserves under a single hold of the lock and returns whether it worked.
I keep only the atomic steps inside the lock and move slow work like I/O, printing and network calls outside, using locals or a snapshot copied under the lock. Independent data can have independent locks. I avoid splitting one compound action across two locks, because that brings the race back.
Lesson: Race conditions: common mistakes and practiceAtomicity is about an action happening as one step, so count++ from two threads does not lose updates. Visibility is about seeing the latest value at all, like a worker noticing a stop flag. A program can have one problem without the other.
synchronized fix a stop flag?No. The visibility promise of synchronized connects an unlock with a later lock of the same object. If the reader does not take the lock, it gets no promise, so both the write and the read must be synchronized on the same object.
volatile make count++ thread-safe?No. count++ is read, add 1, write back, and volatile makes each step visible but does not keep them together. Two threads can read the same value and both write value + 1. I would use a synchronized method or an AtomicInteger.
long and double special for visibility?The Java rules let a JVM write a non-volatile 64-bit long or double as two 32-bit halves, so another thread could see half of the old value and half of the new one. Declaring the field volatile makes each read and write a single step. Most 64-bit JVMs do not split them, but the rules allow it.
It is the Java Memory Model's rule for visibility between actions. If A happens-before B, then B is guaranteed to see A's writes and everything before A. It is not about clock time; two actions with no happens-before between them have no visibility promise.
Lesson: Happens-before in plain wordsjoin() without any locks?The join rule says every action in the worker happens-before join() returns in the waiting thread. So all writes the worker made, even to plain fields and plain collections, are visible afterwards, as long as nobody keeps writing.
No. It is a promise about what the later action sees, not a timeline. The JVM may even reorder work, as long as every action that is supposed to see a write really does.
Lesson: Happens-before in plain wordsfinal fields give across threads?If the constructor finishes without this escaping, any thread that sees a reference to the object sees the correct values of its final fields, even if the reference was shared through a data race. It does not make the reference itself visible, and it does not cover non-final fields.
The JVM sets up a class only when it is first actively used, so Holder and its INSTANCE are created on the first getInstance() call. Class setup runs under a JVM lock and finishes before any thread can use the class, so exactly one object is created and every thread sees it fully built.
Make the class final, every field private final, provide no setters, copy mutable inputs in the constructor, and never hand out a reference to internal mutable data. Also do not let this escape from the constructor. For plain data, a record with copied list components does most of this for me.
Every change creates a new object, so very frequent updates of large objects create garbage and copying work. For data that is read far more often than written, like configs, rates and timetables, that cost is small and the simpler, lock-free reads are worth it.
Lesson: Share immutable objects safelySet the value when the request starts, do the work inside try, and call remove() in finally. That way every request starts clean, even if the previous one threw an exception.
Only when threads do not actually need to share the data. If they must see each other's updates, like a shared counter or seat count, ThreadLocal is wrong because each thread would have its own count. Then I need a lock, an atomic or a thread-safe collection.
Lesson: ThreadLocal for per-thread dataA visibility bug: the flag is a plain field, and on the production JVM the JIT kept its value in a register, so the loop never sees the change. I would make the flag volatile, and check that nothing else in the loop depends on unsynchronized data.
static final SimpleDateFormat in a web app, and what are the fixes?final fixes only the reference; the formatter's internal state is shared, so concurrent requests get wrong dates or exceptions. The best fix is a shared DateTimeFormatter, which is immutable. If I must keep SimpleDateFormat, I use ThreadLocal.withInitial or create one per call.
Thread.sleep better than busy waiting?It uses far less CPU, because the thread sleeps between checks. But it reacts late, up to one sleep interval after the change, and it still wakes up for nothing. A thread that is notified at the moment of the change gives both low CPU use and no delay.
Lesson: Why threads need to wait for each otherThe thread that would make the condition true usually needs the same lock to change the data. It stays BLOCKED while the spinning thread holds the lock, so the condition never changes and the program hangs. That is why waiting must release the lock.
wait() and sleep()?wait() is a method of Object; it must be called while holding that object's lock, it releases the lock while waiting, and it ends on a notify, a timeout or an interrupt. Thread.sleep() is a static method of Thread; it needs no lock, keeps any lock the thread holds and ends only when the time is up or on an interrupt. I use wait to wait for a condition and sleep only to pause.
wait() always be in a while loop?Because when a thread wakes up, its condition is not guaranteed to be true. Another thread may have used up the resource first, notifyAll() may have been sent for a different condition, or the thread may have had a spurious wakeup. The while loop checks again and waits again if needed.
notify() and notifyAll()?notify() wakes one thread from the object's wait set, chosen by the JVM; notifyAll() wakes all of them. Woken threads still have to take the lock one at a time and recheck their condition. notifyAll() is the safe default; notify() saves wake-ups only in specific cases.
It is a notify sent while no thread is waiting, so it has no effect; a thread that starts waiting afterwards can sleep forever. I prevent it by storing the change in shared data, such as a flag, a counter or a queue, and by waiting only while that data says "not yet". A late thread then sees the change and never waits.
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson