Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
Chaturmind
← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 2–5 YearsConcurrency & Multithreading
✓ FreeIntermediate· 9 min read

Synchronization, Locks & Deadlocks — Interview Questions

Runnable vs Thread, protecting shared resources, how synchronized works (monitors, reentrancy, memory effects), method vs block synchronization, volatile and its limits, single-thread deadlock, checking lock ownership, synchronized vs ReentrantLock, exceptions inside synchronized blocks, all the ways to synchronize, and preventing deadlocks.

Published September 25, 2026


How to use this lesson

Concurrency answers at this level must be precise. "volatile makes it thread-safe" or "synchronized is slow" will be challenged. For each tool, know what it guarantees (mutual exclusion, visibility, ordering), and what it doesn't.

Q1. What's the difference between implementing Runnable and extending Thread?

Short answer: Implementing Runnable (or Callable) separates the task from the thread that runs it. Your class can still extend something else, and the same task can run on a raw thread, an ExecutorService or a virtual thread. Extending Thread ties the task to one thread object, uses up your single superclass, and can't be submitted to a thread pool as-is.

Key points to cover:

  • In modern code you rarely create threads directly. You submit Runnable/Callable tasks to executors, or use Thread.ofVirtual() / Executors.newVirtualThreadPerTaskExecutor() (Java 21).

Learn it in depth → Introduction to Java Threads

Q2. How do you ensure a shared resource is accessed safely by multiple threads?

Short answer: Choose the lightest tool that makes every access atomic and visible:

  1. Eliminate the sharing: make objects immutable, confine them to a thread, or keep state in locals.
  2. Use thread-safe classes: AtomicLong/LongAdder, ConcurrentHashMap, BlockingQueue.
  3. Guard compound state with one lock (synchronized or a Lock), covering all reads and writes of that state.
class Inventory {
    private final Map<String, Integer> stock = new HashMap<>();   // guarded by 'this'

    public synchronized boolean reserve(String sku, int qty) {    // check-then-act made atomic
        int available = stock.getOrDefault(sku, 0);
        if (available < qty) return false;
        stock.put(sku, available - qty);
        return true;
    }
    public synchronized int available(String sku) { return stock.getOrDefault(sku, 0); }   // reads locked too
}

Common trap: synchronising only the writes. Unsynchronised reads can see stale or half-updated state.

Q3. How does synchronized work?

Short answer: Every Java object has an intrinsic monitor lock. Entering a synchronized block or method acquires the monitor of the lock object:

  • this for instance methods;
  • the Class object for static methods;
  • the object you name for a block.

Other threads trying to acquire the same monitor block (state BLOCKED) until it's released on exit, whether the exit is normal or through an exception.

Key points to cover:

  • Mutual exclusion: only one thread holds a given monitor at a time.
  • Visibility and ordering: releasing a monitor happens-before the next acquisition of it, so the next thread sees all prior writes.
  • Reentrancy: a thread that already holds a monitor can re-enter code that uses the same lock.
  • At the bytecode level, blocks compile to monitorenter/monitorexit, and methods carry the ACC_SYNCHRONIZED flag. Uncontended locking is cheap on modern JVMs.
  • Java 21 note: a virtual thread that blocks inside synchronized pins its carrier thread. JDK 24 removed most of this pinning.

Learn it in depth → Synchronized and Locks

Q4. What's the difference between a synchronized method and a synchronized block?

Short answer: A synchronized method locks this (or the class) for the whole method. A synchronized block locks only the critical section, on any object you choose. That allows a smaller scope, and separate locks for independent state.

class Stats {
    private final Object countsLock = new Object();
    private final Object latencyLock = new Object();
    private long requests; private long totalMillis;

    void record(long millis) {
        String line = format(millis);                  // done outside any lock
        synchronized (countsLock)  { requests++; }
        synchronized (latencyLock) { totalMillis += millis; }
        log(line);                                     // I/O outside the lock
    }
}

Key points to cover:

  • A private lock object also stops outside code from locking your instance and interfering. With synchronized methods, anyone who can reference the object can lock it too.

Q5. What's the difference between synchronized methods and blocks, in terms of performance and design? (Follow-up)

Short answer: Blocks can reduce contention, because the lock is held only for the shared-state update, not during I/O, logging or computation. That improves throughput under load. Methods are simpler to read and review. Choose blocks when part of the method doesn't touch shared state, or when independent state can use separate locks.

Common trap: splitting locks so much that an invariant spanning two fields is no longer protected. Fields that must stay consistent with each other need the same lock.

Q6. What is volatile, and what does it guarantee?

Short answer: volatile guarantees visibility and ordering for a single variable. Every read sees the most recent write by any thread, and a volatile write happens-before subsequent reads, so writes made before it are also visible to a thread that reads the volatile. Reads and writes of volatile long/double values are also atomic. It doesn't give mutual exclusion.

class ConfigHolder {
    private volatile Config current = Config.load();     // safe publication of immutable snapshots
    Config get() { return current; }
    void reload() { current = Config.load(); }           // readers see either the old or the new, never half-built
}

Learn it in depth → Volatile and the Java Memory Model

Q7. Can volatile replace synchronization?

Short answer: Only for single, independent reads and writes: flags, and publishing an immutable reference. It can't make compound operations atomic: count++ (read, modify, write), check-then-act, or keeping two variables consistent. Those need locks or atomic classes.

private volatile int hits;
void hit() { hits++; }                                  // ❌ lost updates under contention
private final AtomicInteger safeHits = new AtomicInteger();
void safeHit() { safeHits.incrementAndGet(); }          // ✅ an atomic CAS loop
private final LongAdder fastHits = new LongAdder();     // ✅ better under heavy contention

Q8. Can a deadlock happen with a single thread?

Short answer: Not with intrinsic locks, because they're reentrant, so a thread never blocks on a monitor it already holds. But a single thread can block itself forever:

  • It acquires a non-reentrant lock twice. A StampedLock write lock, a Semaphore(1) or a hand-rolled lock all behave this way.
  • It waits on itself, as in Thread.currentThread().join().
  • A task running in a single-threaded executor submits another task to the same executor, and blocks on its Future. The second task can never start.
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> {
    Future<?> inner = single.submit(() -> "never runs");
    return inner.get();                               // blocks forever: the only worker is busy waiting
});

Q9. How do you check whether a thread holds a lock?

Short answer:

  • Thread.holdsLock(obj) tells you whether the current thread holds obj's monitor. It's useful in assertions (assert Thread.holdsLock(this);).
  • For ReentrantLock, use isHeldByCurrentThread(), plus isLocked(), getHoldCount() and getQueueLength().
  • For other threads, take a thread dump (jcmd <pid> Thread.print, jstack), or use ThreadMXBean. They show which monitors each thread holds, and which it's waiting for.

Q10. What's the difference between synchronized and ReentrantLock?

Short answer:

synchronizedReentrantLock
Acquire/releaseAutomatic (block scope)Explicit lock()/unlock() in try/finally
Try without blocking / with timeoutNotryLock(), tryLock(timeout)
Interruptible waitingNolockInterruptibly()
Fairness optionNonew ReentrantLock(true)
Multiple wait conditionsOne wait set per object (wait/notify)Many Conditions (newCondition())
IntrospectionMinimalisLocked, getQueueLength, …
RiskCan't forget to unlockForgetting unlock() leaks the lock
private final ReentrantLock lock = new ReentrantLock();
boolean transfer(Account a, Account b, long amt) throws InterruptedException {
    if (!lock.tryLock(200, TimeUnit.MILLISECONDS)) return false;     // back off instead of waiting forever
    try { a.debit(amt); b.credit(amt); return true; }
    finally { lock.unlock(); }
}

Key points to cover:

  • Default to synchronized for simplicity. Reach for ReentrantLock when you need timeouts, interruptibility, fairness or multiple conditions. ReadWriteLock and StampedLock suit read-heavy data.

Q11. What happens when an exception is thrown inside a synchronized block?

Short answer: The monitor is released automatically as the exception propagates out of the block. The compiler generates an exception handler that runs monitorexit, so other threads aren't blocked forever. The shared state, however, may be left half-updated. Structure the code so it's consistent even when a step fails, or roll back in catch.

Key points to cover:

  • With an explicit Lock, release is your job. Always unlock() in finally.

Q12. What are the different ways to achieve synchronization in Java?

Short answer:

  • Intrinsic locks: synchronized methods and blocks.
  • Explicit locks: ReentrantLock, ReentrantReadWriteLock, StampedLock.
  • volatile, for visibility.
  • Atomic variables: AtomicInteger, AtomicReference, LongAdder (lock-free CAS).
  • Coordinators: Semaphore, CountDownLatch, CyclicBarrier, Phaser, Exchanger.
  • Thread-safe collections: ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue.
  • Immutability and confinement: immutable objects, ThreadLocal.
  • Higher-level models: executors, CompletableFuture, actor or message passing.

Learn it in depth → Concurrent Utilities & Coordination

Q13. What is a deadlock, and how do you prevent it?

Short answer: A deadlock is two or more threads each holding a lock the other needs, and all waiting forever. It requires four conditions: mutual exclusion, hold and wait, no preemption, and circular wait. Break any one of them:

  • Lock ordering: always acquire locks in a global, consistent order (for example, by account ID). This breaks the circular wait.
  • Timeouts: tryLock(timeout), then back off and retry.
  • Fewer locks: avoid holding one lock while calling unknown code (callbacks, listeners), which might take another.
  • Coarser, or lock-free, designs.
void transfer(Account from, Account to, long amount) {
    Account first = from.id() < to.id() ? from : to;      // consistent order: no circular wait
    Account second = first == from ? to : from;
    synchronized (first) {
        synchronized (second) {
            from.debit(amount);
            to.credit(amount);
        }
    }
}

Key points to cover:

  • Detect deadlocks with a thread dump. The JVM prints "Found one Java-level deadlock" for monitor and ReentrantLock cycles. ThreadMXBean.findDeadlockedThreads() finds them programmatically.

Learn it in depth → Deadlock, Starvation & Livelock

Follow-up questions this topic invites — and their answers

Q: What is a livelock? A: Threads keep changing state in response to each other, for example both backing off and retrying in lockstep, so no progress is made, although nothing is blocked. Randomised backoff breaks the symmetry.

Q: What is starvation? A: A thread never gets the CPU time or lock it needs, because others keep taking it. For example, unfair locks under heavy contention, or low-priority threads. Fair locks or bounded work queues help.

Q: Why should you never synchronize on a String literal or a boxed Integer? A: They're shared: interned strings, and cached Integers from −128 to 127. Unrelated code that locks on the same value then contends on, or deadlocks with, yours. Use a private final Object lock.

Q: What's the double-checked locking bug, and how is it fixed? A: Without volatile, another thread can see a non-null reference to an object whose constructor hasn't finished yet (because of reordering). Declaring the field volatile fixes it. The holder-class idiom avoids the problem entirely.

Previous

Stream Coding Problems (Level 2, Part 2) — Interview Questions

Next

Executors, ThreadLocal & Concurrent Collections — Interview Questions

AI Tutor

Lesson: Synchronization, Locks & Deadlocks — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.