HuntExams Academy logo
JavaIntermediate#concurrency

What is the difference between synchronized methods and synchronized blocks?

Answer

Both use the same intrinsic monitor lock. The difference is which object is locked and for how long. A synchronized method locks an implicit object for the whole method body: - an instance method locks this - a static method locks the Class object, MyClass.class A synchronized block lets you choose the lock and narrow the critical section: synchronized (lockObject) { only the lines that need protection } Why blocks are usually preferred: 1. A shorter critical section means less contention and better throughput \u2014 expensive I/O, logging and validation stay outside the lock. 2. You control the lock object, so you can use a private final Object lock, or separate locks for independent fields (lock splitting), instead of exposing this. Locking on this means any external code holding a reference to your object can lock it and stall or deadlock you. 3. You can lock a different object entirely when guarding a shared external structure. Points interviewers push on: - Both are reentrant: a thread already holding the lock can re-enter. - Both give the same happens-before visibility guarantees \u2014 entering acquires, exiting releases \u2014 so writes are visible to the next thread. - A synchronized instance method and a static synchronized method use DIFFERENT locks and do not exclude each other. - Never lock on a String literal, a boxed Integer, or a mutable field: literals and wrapper caches are shared globally, and reassigning the field changes the lock mid-flight. - Bytecode differs: methods carry the ACC_SYNCHRONIZED flag, blocks emit monitorenter/monitorexit. - For tryLock, timeouts, fairness, multiple conditions or read/write separation, move to java.util.concurrent \u2014 ReentrantLock, ReentrantReadWriteLock, StampedLock.

Example

class Counter {
    private int count;
    private final Object lock = new Object();   // private lock, not `this`

    // Entire method locked on this
    public synchronized void incrementMethod() {
        count++;
    }

    // Only the critical section is locked
    public void incrementBlock(String payload) {
        String cleaned = payload.trim().toLowerCase();   // outside the lock
        log.debug("incrementing for {}", cleaned);       // outside the lock
        synchronized (lock) {
            count++;                                     // the only guarded line
        }
    }

    // Locks Counter.class, NOT this
    public static synchronized void resetStats() { /* ... */ }
}

// Lock splitting: independent fields, independent locks
class Stats {
    private final Object readsLock  = new Object();
    private final Object writesLock = new Object();
    private long reads, writes;
    void read()  { synchronized (readsLock)  { reads++;  } }
    void write() { synchronized (writesLock) { writes++; } }
}

// Never do this -- an interned, globally shared lock
// synchronized ("LOCK") { }

Detailed explanation

Synchronized methods and synchronized blocks use Java’s intrinsic monitor mechanism to provide mutual exclusion and memory visibility. Their main differences are which object supplies the monitor and how much code executes while holding it. An instance synchronized method acquires the monitor of this; a static synchronized method acquires the monitor of the declaring class’s Class object, such as Counter.class. A synchronized block explicitly selects its monitor using synchronized (lockObject) { ... }. Threads exclude one another only when they acquire the same monitor, so instance and static synchronized methods do not automatically block each other.

A synchronized method holds its monitor throughout the method body. A block can protect only the operations that must execute atomically, leaving unrelated computation, logging or expensive I/O outside. This can reduce contention, but moving work outside is safe only when it does not depend on protected state remaining unchanged. For example, checking a shared balance and deducting money must stay within one critical section. Blocks also support private final Object locks, preventing external callers from directly acquiring an internal monitor as they could with this.

Both forms are reentrant: a thread already holding a monitor can acquire it again. Both release the monitor on normal completion or when an exception exits the protected region. Under the Java Memory Model, unlocking a monitor happens-before a subsequent lock of that same monitor, making earlier writes visible to the acquiring thread. Under the hood, synchronized methods carry the ACC_SYNCHRONIZED bytecode flag, while blocks use monitorenter and monitorexit instructions, with exception handling ensuring release.

The crucial interview detail is consistent locking. In the supplied Counter example, incrementMethod protects count using this, whereas incrementBlock uses lock; concurrent calls can therefore lose updates. Both paths, and relevant reads, must follow one synchronization policy. Separate locks suit independent state, not invariants spanning both fields. Avoid string literals, boxed values and reassignable lock references. Blocks offer flexibility, but methods remain clear when the entire operation needs one monitor; advanced requirements such as timed acquisition call for explicit concurrency utilities.

Key points to remember

  • Instance synchronized methods lock this; static synchronized methods lock the declaring class’s Class object.
  • Synchronized blocks choose an explicit monitor and can narrow the critical section.
  • Both forms provide reentrancy, automatic release on exit and identical same-monitor visibility guarantees.
  • All accesses participating in a shared-state invariant must follow a consistent synchronization policy.
  • Methods use ACC_SYNCHRONIZED; blocks use monitorenter and monitorexit bytecode instructions.

Common mistakes candidates make

  • Assuming different locks protect the same field: the supplied Counter methods use different monitors and are not mutually exclusive.
  • Moving state-dependent validation outside the lock, separating a check from the update it must protect.
  • Locking on string literals, boxed values or reassignable references, creating unintended sharing or inconsistent monitor selection.

Likely follow-up questions

Is a synchronized instance method equivalent to a synchronized block on this?

For mutual exclusion and visibility, wrapping the entire method body in synchronized (this) provides equivalent monitor protection. The bytecode representation differs, and a smaller block changes how long the monitor is held.

Are synchronized blocks always faster than synchronized methods?

No; both use the same intrinsic locking mechanism. A block can improve concurrency by reducing unnecessary time under the lock, but performance depends on contention, workload and JVM optimisations.

When should you use ReentrantLock instead?

Use ReentrantLock when you need features such as tryLock, timed or interruptible acquisition, configurable fairness or multiple Condition objects. Unlike synchronized, it requires explicit unlocking, normally in a finally block.

Interview tip: Start with “same mechanism, different monitor selection and scope,” then demonstrate why protecting one field with two different locks is unsafe.

Related Interview Questions

1
JavaIntermediate#concurrency

What are atomic classes like AtomicInteger used for?

Open
2
JavaAdvanced#concurrency

What is a CountDownLatch?

Open
3
JavaAdvanced#concurrency

What is a ThreadLocal and when would you use it?

Open