Java
volatile
final
synchronized object
multithreading

Java Is volatile / final required for reference to synchronized object?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

If an object is used as a lock in synchronized blocks, developers often ask whether the reference to that object must also be declared volatile or final. The short answer is no, not always, but the reference still needs safe publication and stable usage.

volatile, final, and synchronized solve different problems. The right modifier depends on whether the reference ever changes and how other threads learn about it.

What synchronized Already Guarantees

When two threads synchronize on the same monitor object, Java provides mutual exclusion and memory-visibility guarantees for code protected by that monitor.

java
1public class Counter {
2    private final Object lock = new Object();
3    private int value = 0;
4
5    public void increment() {
6        synchronized (lock) {
7            value++;
8        }
9    }
10
11    public int get() {
12        synchronized (lock) {
13            return value;
14        }
15    }
16}

Here, synchronized is what protects value. The memory effects come from entering and exiting the monitor, not from volatile on value or on lock.

Does the Lock Reference Need volatile?

If the lock reference never changes after construction, it does not need to be volatile. In fact, it is often best as final:

java
private final Object lock = new Object();

Why final? Because it expresses the design clearly: this lock object is fixed and should never be reassigned.

If the reference can change after publication and different threads may observe those changes without other synchronization, then the reference itself has a visibility problem. In that case, volatile or some other safe-publication mechanism may be needed for the reference, but changing lock references is usually a bad design idea in the first place.

Why final Is Often the Best Choice

Using final for a lock reference gives two benefits:

  • it prevents accidental reassignment
  • it helps safe publication after construction when the object itself is published correctly

A stable lock object matters because synchronized(lock) only works if all relevant threads really synchronize on the same object. If one thread sees a different lock reference, you no longer have one shared monitor.

That is why lock references are commonly written as private final Object lock = new Object();.

When volatile Matters

volatile is relevant when the reference itself is read and written across threads without synchronization and you need those updates to become visible.

Example:

java
private volatile Config currentConfig;

That is a different use case from a fixed lock object. A configuration reference may legitimately be replaced. A lock reference usually should not be.

So if the question is specifically about a normal synchronization object used as a monitor, volatile is typically unnecessary and final is the better fit.

Safe Publication Still Matters

Even when the lock field is final, the containing object must still be published safely to other threads. In ordinary Java application code, that usually happens naturally through proper construction, thread startup, dependency injection, or other standard object-sharing patterns.

The core point is that synchronized protects the critical section only after all threads are actually looking at the same object graph.

A Bad Pattern: Reassignable Lock Objects

Consider this:

java
1private Object lock = new Object();
2
3public void replaceLock() {
4    lock = new Object();
5}

This is dangerous. One thread might synchronize on the old lock while another synchronizes on the new one, which defeats mutual exclusion entirely.

In such a design, adding volatile to lock does not fix the conceptual problem. It only makes the changing reference more visible. The real bug is that the lock object should not have been replaceable.

Common Pitfalls

One common mistake is assuming volatile and synchronized are interchangeable. They are not. volatile handles visibility of single-variable reads and writes; synchronized also provides mutual exclusion.

Another issue is using a mutable lock reference. Lock identity should be stable, so final is usually the right expression of intent.

It is also easy to think that if the protected data is synchronized, the lock reference can be managed casually. In reality, all threads must coordinate through the exact same monitor object.

Finally, do not expose your lock object publicly. Keep it private so outside code cannot synchronize on it and interfere with your concurrency design.

Summary

  • A reference to a synchronization object is usually best declared final, not volatile.
  • 'synchronized provides the mutual exclusion and visibility guarantees for code guarded by that monitor.'
  • 'volatile only matters if the reference itself is intentionally changing across threads without other synchronization.'
  • Reassigning lock objects is usually a design bug rather than something volatile should solve.
  • The important requirement is that all threads synchronize on the same stable monitor object.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track 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.

Browse interview questions

All Rights Reserved.