In Java critical sections, what should I synchronize on?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In Java, the object you synchronize on is the lock that protects a critical section. The correct lock is the one shared by every thread that touches the same mutable state. Most synchronization bugs happen because the code locks on the wrong object, exposes the lock publicly, or uses multiple unrelated locks to guard the same data.
Synchronize on the Object That Guards the Shared State
A critical section exists because multiple threads can access the same mutable data. The lock must be tied to that shared data, not chosen arbitrarily.
Simple example:
Here the monitor is this, and it works because the protected state belongs to the instance itself.
Prefer a Private Lock Object for Internal State
Synchronizing on this is common, but a private lock object is often safer because external code cannot accidentally lock on it.
This is a strong default for classes whose locking policy should remain entirely internal.
Do Not Synchronize on Publicly Accessible Objects
Avoid synchronizing on objects that other code can also lock:
- string literals
- boxed primitives
- publicly exposed collections
- objects returned from getters
Bad example:
String literals are interned, so unrelated code may accidentally share the same lock. That can create surprising contention or deadlocks.
Keep One Lock Per Protected State Group
If two fields must change together to remain consistent, they should usually be protected by the same lock.
Using separate locks for data that must remain consistent together can create race conditions even though each field is individually synchronized.
Synchronize the Smallest Useful Critical Section
Lock only the code that truly needs exclusive access. The longer the critical section, the more threads block each other.
Here the expensive or slow external action happens outside the lock. That reduces contention.
Consider Higher-Level Concurrency Tools
Not every critical section should use synchronized. Java’s concurrent utilities are often better:
- '
ReentrantLockwhen you need advanced lock control' - '
ReadWriteLockfor read-heavy workloads' - '
AtomicIntegerfor simple counters' - concurrent collections such as
ConcurrentHashMap
Example with AtomicInteger:
This avoids explicit monitor locking for a simple counter.
Do Not Lock on Objects You Do Not Own
If a collection or dependency is passed in from outside, synchronizing on it can be dangerous because other code may use the same object for unrelated locking.
Safer pattern:
Owning the lock object makes the concurrency policy clearer.
Common Pitfalls
The most common mistake is using different locks to protect the same state. Threads then appear synchronized, but they are not actually coordinating with each other.
Another issue is synchronizing on this in a type that is widely exposed, which lets outside code interfere with internal locking.
Developers also often hold locks while doing slow I/O or remote calls. That can create unnecessary contention and even deadlocks.
Summary
- Synchronize on the lock that truly guards the shared mutable state.
- A private final lock object is usually the safest default.
- Avoid locking on public, shared, or interned objects such as string literals.
- Keep related state under the same lock if consistency depends on it.
- Prefer higher-level concurrency utilities when they express the problem more clearly than raw
synchronized.
Related reading
- In Java, how can I ensure safe and consistent concurrent usage of a boolean flag while minimizing time performance impact?
- In Java, how do you determine if a thread is running?
- In Javascript / ES6, how do I wait for Python code to finish executing in a Jupyter Notebook?
- In .NET, what thread will Events be handled in?
- In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?
- In Java, how do I efficiently and elegantly stream a tree node''s descendants?
- In .NET, what thread will Events be handled in?
- In Python, what is the difference between async for x in async_iterator and for x in await async_iterator?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.