Synchronization
Synchronization Interview with follow-up questions
1. What is synchronization in the context of Java multithreading?
Synchronization is the mechanism that controls concurrent access to shared mutable state, guaranteeing two things: mutual exclusion (only one thread executes a critical section at a time) and visibility (a thread sees other threads' updates, per the Java Memory Model's happens-before rules). It prevents race conditions and keeps data consistent.
In Java the built-in mechanism is the intrinsic lock (monitor) every object has, taken via synchronized:
synchronized (lockObject) { // acquire monitor on lockObject
// critical section — one thread at a time
} // release on exit (even on exception)
The points interviewers want: locks in Java are reentrant (a thread can re-acquire a lock it already holds); a synchronized block releases the lock automatically when it exits (normally or via exception); and synchronization provides both the mutual exclusion and the memory-visibility guarantee — which is why volatile (visibility only) isn't a substitute for atomic compound updates.
The modern framing: beyond synchronized, Java's java.util.concurrent offers more flexible options — explicit Lock/ReentrantLock (tryLock, timed, fair, interruptible), ReadWriteLock, atomic variables, and concurrent collections — which are preferred for complex coordination. Best practice is to minimize shared mutable state (favor immutability and high-level utilities), and keep critical sections small to limit contention. A current note: as of Java 21+, synchronized no longer pins virtual threads, so it composes cleanly with Loom.
Follow-up 1
Can you explain how synchronization can help in handling thread interference?
Thread interference occurs when two or more threads access shared data simultaneously, resulting in unpredictable and incorrect behavior. Synchronization can help in handling thread interference by allowing only one thread to access the shared data at a time. By using synchronization, we can ensure that the shared data is accessed in a mutually exclusive manner, preventing race conditions and maintaining data integrity.
Follow-up 2
What are the different ways to achieve synchronization in Java?
There are multiple ways to achieve synchronization in Java:
Synchronized methods: By using the
synchronizedkeyword before a method declaration, we can make the method synchronized. Only one thread can execute the synchronized method at a time.Synchronized blocks: By using the
synchronizedkeyword before a block of code, we can make the block synchronized. Only one thread can execute the synchronized block at a time.Locks and conditions: Java provides the
Lockinterface and its implementations, such asReentrantLock, which can be used to achieve synchronization. Locks provide more flexibility and features compared to synchronized methods and blocks.Atomic classes: Java provides atomic classes, such as
AtomicIntegerandAtomicBoolean, which can be used to perform atomic operations without the need for explicit synchronization.
The choice of synchronization mechanism depends on the specific requirements of the application.
Follow-up 3
What is the role of synchronized keyword in Java?
The synchronized keyword in Java is used to achieve synchronization. It can be applied to methods or blocks of code. When applied to a method, the entire method becomes synchronized, and only one thread can execute the method at a time. When applied to a block of code, only the block of code is synchronized, allowing more fine-grained control over synchronization. The synchronized keyword ensures that only one thread can access the synchronized code at a time, preventing thread interference and maintaining data consistency.
Follow-up 4
Can you give an example where synchronization is necessary?
Synchronization is necessary in situations where multiple threads access shared resources or critical sections of code. For example, consider a scenario where multiple threads are updating a shared counter variable. Without synchronization, race conditions can occur, leading to incorrect results. By using synchronization, we can ensure that only one thread can access the counter variable at a time, preventing race conditions and maintaining the correctness of the counter.
2. How does the 'synchronized' keyword work in Java?
synchronized enforces mutual exclusion using an object's intrinsic lock (monitor). A thread must acquire the monitor to enter a synchronized method/block and releases it on exit (including on exception); other threads trying to acquire the same monitor block until it's free. It also creates a happens-before relationship, so changes made under the lock are visible to the next thread that acquires it.
Which lock is used depends on the form:
synchronized void m() { ... } // locks 'this' (the instance)
static synchronized void s() { ... } // locks the Class object
synchronized (lock) { ... } // locks the given object
Key behaviors interviewers want: the lock is reentrant (the holding thread can re-enter without deadlocking itself); only one thread per monitor proceeds, but threads using different lock objects don't block each other; and synchronized guards both mutual exclusion and visibility. wait()/notify()/notifyAll() (called while holding the monitor) let threads coordinate within it.
The practical guidance and 2026 note: lock on a private, dedicated object (private final Object lock = new Object();) rather than this or a public/String/Integer object, to avoid external interference and surprises; keep critical sections small to reduce contention; and for advanced needs prefer ReentrantLock (timed/interruptible/fair) or atomics. Also, since Java 21+, blocking in a synchronized block no longer pins a virtual thread's carrier — removing a former Loom limitation.
Follow-up 1
What happens if a thread encounters a synchronized method?
When a thread encounters a synchronized method, it tries to acquire the lock associated with the object on which the method is defined. If the lock is available, the thread acquires the lock and proceeds with the execution of the method. If the lock is already held by another thread, the thread enters a blocked state and waits until the lock becomes available. Once the lock is acquired, the thread executes the synchronized method and releases the lock when it completes execution.
Follow-up 2
Can you explain the concept of a monitor lock or mutex?
In Java, a monitor lock, also known as a mutex (short for mutual exclusion), is used to provide synchronization and ensure that only one thread can execute a synchronized method or block at a time. When a thread encounters a synchronized method or block, it tries to acquire the monitor lock associated with the object or class on which the method or block is synchronized. If the lock is available, the thread acquires the lock and proceeds with the execution. If the lock is already held by another thread, the thread enters a blocked state and waits until the lock becomes available. Once the lock is acquired, the thread executes the synchronized method or block and releases the lock when it completes execution.
Follow-up 3
What is the scope of synchronized blocks?
In Java, synchronized blocks are used to create mutually exclusive sections of code within a method or a block. The scope of a synchronized block is determined by the object or class on which it is synchronized. When a thread encounters a synchronized block, it tries to acquire the lock associated with the specified object or class. If the lock is available, the thread acquires the lock and proceeds with the execution of the synchronized block. If the lock is already held by another thread, the thread enters a blocked state and waits until the lock becomes available. Once the lock is acquired, the thread executes the synchronized block and releases the lock when it completes execution.
Follow-up 4
Can we use synchronized keyword with variables?
No, the synchronized keyword cannot be used with variables in Java. It can only be used with methods or blocks of code. The purpose of using synchronized keyword is to provide mutual exclusion and ensure thread safety while accessing shared resources or modifying shared data. To synchronize access to variables, you can use other synchronization mechanisms such as locks or atomic variables.
3. What is the difference between synchronized method and synchronized block in Java?
Both use an object's intrinsic lock, but differ in granularity and which lock is held:
- Synchronized method — the whole method body is the critical section. An instance method locks
this; astaticsynchronized method locks theClassobject. The lock is held for the method's entire duration. - Synchronized block — locks only a chosen object for a specific range of statements:
synchronized (lock) { ... }. This gives finer control — you can lock just the lines that touch shared state, and choose the lock object.
synchronized void update() { a++; b++; } // locks 'this' for the whole method
void update() {
doUnsharedWork(); // runs without the lock
synchronized (lock) { a++; b++; } // lock only the critical section
}
Why the block is usually better (what interviewers reward): it minimizes the critical section, reducing contention and improving throughput; it lets you lock a private dedicated lock object (private final Object lock = new Object();) instead of this, avoiding interference from external code that might synchronize on your instance; and it can use different locks for independent data to allow more parallelism. The takeaway: synchronized methods are simpler but coarse; synchronized blocks give finer, safer control — prefer narrow blocks on private lock objects, and reach for ReentrantLock/atomics when you need more than the intrinsic monitor offers.
Follow-up 1
Can you explain with an example?
Sure! Here's an example to illustrate the difference between synchronized methods and synchronized blocks:
public class SynchronizedExample {
private int count = 0;
private Object lock = new Object();
public synchronized void synchronizedMethod() {
// Code that requires synchronization
count++;
}
public void synchronizedBlock() {
synchronized (lock) {
// Code that requires synchronization
count++;
}
}
}
In this example, the synchronizedMethod is a synchronized method that acquires the lock on the entire SynchronizedExample object instance. This means that only one thread can execute the synchronizedMethod at a time.
On the other hand, the synchronizedBlock is a synchronized block that acquires the lock on the lock object reference. This means that only one thread can enter the synchronizedBlock for a given SynchronizedExample object instance, while other threads can still access other synchronized blocks or synchronized methods of the same object instance.
Follow-up 2
In what scenarios would you use a synchronized block instead of a synchronized method?
There are a few scenarios where using a synchronized block might be preferred over using a synchronized method:
Fine-grained locking: If you only need to synchronize a small portion of a method or a code block, using a synchronized block allows you to limit the scope of the lock to only that portion. This can help improve performance by reducing contention and allowing other threads to access non-synchronized parts of the code.
Synchronizing on different objects: If you need to synchronize different parts of a class on different objects, using synchronized blocks allows you to specify different object references for each block. This can be useful in scenarios where different parts of the class have different synchronization requirements.
Avoiding deadlock: In some cases, using synchronized blocks with careful ordering of locks can help avoid deadlock situations. By acquiring and releasing locks in a specific order, you can prevent threads from getting stuck in a deadlock where they are waiting for each other's locks.
Follow-up 3
What is the impact on performance in both cases?
The impact on performance of using synchronized methods and synchronized blocks can vary depending on the specific scenario.
In general, synchronized methods tend to have a higher overhead compared to synchronized blocks. This is because synchronized methods acquire and release the lock on the entire object instance, which can potentially block other threads from accessing any synchronized method of the same object instance.
On the other hand, synchronized blocks allow for more fine-grained locking and can potentially reduce contention and improve performance. By limiting the scope of the lock to a specific code block, other threads can still access non-synchronized parts of the code, reducing the chances of contention and improving concurrency.
However, it is important to note that the impact on performance can also depend on factors such as the number of threads, the complexity of the synchronized code, and the specific hardware and JVM implementation. It is recommended to measure and profile the application to determine the actual impact on performance.
4. What is the role of 'volatile' keyword in the context of synchronization?
volatile addresses the visibility and ordering half of synchronization, but not mutual exclusion. Declaring a field volatile guarantees that reads/writes go directly to main memory (no per-thread caching of that variable) and establishes a happens-before relationship, so a write by one thread is promptly visible to others and isn't reordered around.
private volatile boolean stop = false; // safe cross-thread flag
public void shutdown() { stop = true; } // other threads see it promptly
The critical correction interviewers are testing (the source's claim is wrong): volatile does NOT make compound operations atomic. Reads and writes of the variable itself are atomic (including long/double, which otherwise aren't guaranteed atomic), but count++ is read-modify-write and is not atomic even when count is volatile — two threads can still lose updates. For atomic compound updates use AtomicInteger/AtomicLong (or synchronized/locks).
So the role of volatile in synchronization: it provides visibility/ordering for a single variable without locking — ideal for status flags, the safe publication of an immutable object reference, and the classic double-checked-locking singleton. It is not a replacement for synchronized/locks when you need mutual exclusion or multi-step atomicity. The crisp distinction: synchronized = mutual exclusion + visibility; volatile = visibility/ordering only (no atomicity for compound actions).
Follow-up 1
How does volatile differ from synchronized?
While both 'volatile' and 'synchronized' are used for synchronization in Java, they have different purposes and behaviors:
'volatile' is used to ensure the visibility of variables across multiple threads. It guarantees that any read or write operation on a volatile variable is atomic and that the variable's value is always read from the main memory. However, 'volatile' does not provide mutual exclusion, meaning that it does not prevent multiple threads from accessing and modifying the variable simultaneously.
'synchronized' is used to provide mutual exclusion and ensure both atomicity and visibility of variables. It guarantees that only one thread can execute a synchronized block or method at a time, preventing concurrent access and modification of shared data. In addition, 'synchronized' also ensures that changes made by one thread are visible to other threads.
Follow-up 2
Can you give an example of using volatile?
Sure! Here's an example of using the 'volatile' keyword:
public class SharedCounter {
private volatile int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
In this example, the 'count' variable is declared as volatile. This ensures that any changes made to the 'count' variable by one thread are immediately visible to other threads. Without the 'volatile' keyword, different threads may have their own cached copies of the 'count' variable, leading to inconsistencies in the value of 'count'.
Follow-up 3
When should we use volatile instead of synchronized?
You should use 'volatile' instead of 'synchronized' when:
You only need to ensure the visibility of a variable across multiple threads, but not mutual exclusion. If you have a variable that is frequently read by multiple threads but rarely modified, using 'volatile' can provide better performance compared to using 'synchronized'.
You are working with a boolean flag or a single variable that is updated by one thread and read by multiple threads. In such cases, using 'volatile' can be more efficient than using 'synchronized'.
However, if you need to ensure mutual exclusion and atomicity of operations, or if you are working with multiple variables that need to be updated atomically, 'synchronized' is the appropriate choice.
5. What are the disadvantages of synchronization?
Synchronization is necessary for correctness, but it has real costs:
- Performance overhead — acquiring/releasing locks isn't free, and a contended lock forces threads to block and context-switch.
- Reduced concurrency/scalability — a critical section serializes access, creating a bottleneck; over-synchronizing (coarse locks) limits how much work runs in parallel.
- Thread contention — many threads competing for one lock spend time waiting rather than working.
- Deadlock — two threads each holding a lock the other needs block forever (and related hazards: livelock, starvation, priority inversion).
- Increased complexity — concurrent code with manual locking is hard to reason about, test, and debug (bugs are intermittent and timing-dependent).
Thread A: lock(X) ... wants lock(Y)
Thread B: lock(Y) ... wants lock(X) → deadlock
The framing interviewers reward — how to mitigate these:
- Minimize shared mutable state (favor immutability and local/
ThreadLocaldata) so you need less locking. - Keep critical sections small; acquire locks in a consistent global order to prevent deadlock; use timed/
tryLockto detect it. - Prefer
java.util.concurrentover manualsynchronized: atomics (AtomicInteger), concurrent collections (ConcurrentHashMap), andReadWriteLock/StampedLockfor read-heavy data — these reduce contention and avoid blocking.
So synchronization trades throughput and simplicity for safety; the goal is to use the least and finest-grained synchronization that still guarantees correctness, leaning on lock-free/immutable designs where possible.
Follow-up 1
Can you explain how synchronization can lead to thread contention?
Synchronization can lead to thread contention when multiple threads are competing for the same lock or resource. When a thread encounters a synchronized block or method, it needs to acquire the associated lock before entering the critical section. If another thread already holds the lock, the waiting thread is blocked and put into a waiting state until the lock is released. This waiting and blocking of threads can lead to thread contention, where multiple threads are waiting for the same lock, resulting in reduced performance.
Follow-up 2
What is deadlock and how can it occur due to synchronization?
Deadlock is a situation where two or more threads are blocked indefinitely, waiting for each other to release locks or resources. Deadlock can occur due to synchronization when multiple threads acquire locks in different orders and then wait for each other to release the locks. This can create a circular dependency, where each thread is waiting for a lock that is held by another thread in the cycle. As a result, none of the threads can make progress and the application becomes stuck in a deadlock state.
Follow-up 3
How can we prevent deadlock situation?
There are several techniques to prevent deadlock situations:
- Lock ordering: Ensure that threads always acquire locks in a consistent and predefined order to avoid circular dependencies.
- Lock timeout: Use lock timeouts to prevent threads from waiting indefinitely. If a thread cannot acquire a lock within a certain time period, it can release the lock and try again later.
- Resource allocation graph: Analyze the resource allocation graph to identify potential circular dependencies and restructure the code to avoid them.
- Avoidance of nested locks: Avoid acquiring multiple locks within a single thread to reduce the chances of deadlock.
- Deadlock detection and recovery: Implement deadlock detection algorithms to identify and recover from deadlock situations.
Follow-up 4
What is the impact of synchronization on performance?
Synchronization can have both positive and negative impacts on performance:
- Positive impact: Synchronization ensures thread safety and prevents data races, which can lead to incorrect results or unpredictable behavior. It allows multiple threads to safely access shared resources without causing data corruption.
- Negative impact: Synchronization introduces overhead due to acquiring and releasing locks. This overhead can reduce the overall performance of the application, especially in scenarios with high contention for locks. Additionally, synchronization can limit the scalability of an application by introducing a bottleneck, as only one thread can access a critical section at a time.
Live mock interview
Mock interview: Synchronization
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.