Question
What `synchronized` Means in Java: Thread Safety Explained
Question
I have several questions about the synchronized keyword in Java.
- What is the purpose of the
synchronizedkeyword? - When should a method or block be synchronized?
- What does
synchronizedmean both programmatically and logically in multithreaded code?
Short Answer
By the end of this page, you will understand what synchronized does in Java, why it matters in multithreaded programs, when to use it, and how it prevents multiple threads from changing shared data at the same time. You will also see practical examples, common mistakes, and how developers use synchronization in real codebases.
Concept
synchronized is a Java keyword used to control access to shared data when multiple threads are running at the same time.
In a multithreaded program, two or more threads may try to read or modify the same object or variable. If this happens without coordination, the program can produce incorrect results. This kind of problem is called a race condition.
synchronized helps by allowing only one thread at a time to execute a specific critical section of code for a given lock.
What it does
When a thread enters synchronized code, it must first acquire a monitor lock.
- If the lock is free, the thread enters the block or method.
- If another thread already holds the lock, the current thread waits.
- When the first thread leaves the synchronized code, it releases the lock.
Why this matters
Without synchronization, operations that look simple may actually happen in multiple steps.
For example, this is not atomic:
count++;
It is roughly:
- Read
count - Add 1
- Write the new value back
If two threads do this at the same time, one update can be lost.
Programmatically
synchronized means:
- acquire a lock before entering the protected code
Mental Model
Imagine a shared bathroom with one key.
- The bathroom is the shared resource.
- The key is the lock.
- A person is a thread.
If someone has the key, no one else can enter. Others must wait until the key is returned.
That is what synchronized does: it makes threads take turns when using shared data.
A second way to think about it is a notebook on a desk shared by multiple people.
- If everyone writes at the same time, the notes become corrupted.
- If only one person can use the notebook at a time, the data stays consistent.
So synchronized is Java's way of saying: only one thread may work with this shared state at a time.
Syntax and Examples
Synchronized instance method
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
Here:
- both methods lock on the current object (
this) - if one thread is inside
increment(), another thread cannot enterincrement()orgetCount()on the same object at the same time
Synchronized block
class Counter {
private int count = 0;
private final Object ();
{
(lock) {
count++;
}
}
{
(lock) {
count;
}
}
}
Step by Step Execution
Consider this example:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
Suppose two threads, Thread-A and Thread-B, both call increment() on the same Counter object.
Step-by-step
Thread-Atries to enterincrement().- Because the method is
synchronized, Java checks whether the object's lock is available. - The lock is free, so
Thread-Aacquires it. Thread-Aexecutescount++.- While
Thread-Ais still inside the method,Thread-Btries to enter .
Real World Use Cases
synchronized is used when multiple threads access the same mutable data.
Common practical uses
-
Counters and metrics
- tracking requests processed
- counting tasks completed
-
Banking or wallet logic
- deposits
- withdrawals
- balance updates
-
Shared in-memory caches
- adding or updating values safely
-
Producer-consumer systems
- coordinating access to shared queues or buffers
-
Configuration reload or state updates
- preventing partial updates to shared settings
-
Legacy Java server code
- protecting mutable singleton objects used by multiple request threads
Example scenario
A web server may handle many requests at once. If all requests update the same shared object, unsynchronized access can corrupt its state. Synchronization ensures that state changes happen safely.
Real Codebase Usage
In real projects, developers rarely mark everything as synchronized. Instead, they apply it carefully around shared mutable state.
Common patterns
Guarding a critical section
synchronized (lock) {
sharedMap.put(key, value);
}
Only the part that touches shared data is locked.
Guard clauses inside synchronized code
public synchronized void withdraw(int amount) {
if (amount <= 0) {
return;
}
if (balance < amount) {
return;
}
balance -= amount;
}
This keeps business rules clear while still protecting shared state.
Private lock object
private final Object lock = new Object();
Many developers prefer a private lock instead of synchronized(this) because outside code cannot accidentally lock on it.
Common Mistakes
1. Synchronizing when there is no shared state
If a variable is local to a method, each thread gets its own copy.
public void doWork() {
int x = 0; // local variable, not shared
x++;
}
This does not need synchronization.
2. Locking the wrong object
public void increment() {
synchronized (new Object()) {
count++;
}
}
This is broken because each call creates a new lock object. Threads are not actually coordinating with each other.
Use a shared lock:
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
3. Assuming makes everything thread-safe
Comparisons
| Concept | What it does | Best for | Notes |
|---|---|---|---|
synchronized method | Locks the whole method | Simple object-level protection | Easy to read, less flexible |
synchronized block | Locks a specific section | Fine-grained locking | Usually preferred for control |
volatile | Ensures visibility of a variable | Flags and simple state sharing | Does not make compound actions atomic |
AtomicInteger | Atomic numeric operations | Counters, increments | Good alternative to synchronized counters |
ReentrantLock |
Cheat Sheet
Quick reference
Synchronized instance method
public synchronized void method() {
// locks on this
}
Synchronized static method
public static synchronized void method() {
// locks on ClassName.class
}
Synchronized block
synchronized (lockObject) {
// critical section
}
Key rules
- Use it only when threads share mutable data.
- Only one thread at a time can hold a given lock.
- Threads must use the same lock to coordinate properly.
synchronizedprovides both:- mutual exclusion
- visibility of changes
Good candidates for synchronization
- updating shared counters
- modifying shared collections
- read-modify-write operations
- business rules involving shared state
Avoid when
FAQ
What does synchronized mean in Java?
It means only one thread at a time can execute a protected block or method for a specific lock.
When should I use synchronized?
Use it when multiple threads access the same mutable data and at least one thread writes to that data.
Does synchronized make code faster?
Not usually. Its main purpose is correctness, not speed. In some cases it can reduce performance because threads must wait.
Is synchronized the same as volatile?
No. volatile ensures visibility of a variable's latest value, but it does not protect multi-step operations like count++.
Should I synchronize every method?
No. Synchronize only the parts that access shared mutable state. Overusing it can make code slower and harder to maintain.
What object gets locked in a synchronized method?
For an instance method, Java locks this. For a static synchronized method, Java locks the class object.
Can synchronized prevent race conditions?
Yes, if all threads use the same lock consistently when accessing the shared state.
Is synchronized still used in modern Java?
Mini Project
Description
Build a small Java program that simulates a shared bank account accessed by multiple threads. This project demonstrates why synchronized matters when several threads update the same data at the same time.
Goal
Create a thread-safe bank account where concurrent deposits and withdrawals do not corrupt the balance.
Requirements
- Create a
BankAccountclass with a sharedbalancefield. - Add
depositandwithdrawmethods that safely modify the balance. - Start multiple threads that perform deposits and withdrawals on the same account.
- Print the final balance after all threads finish.
- Ensure the final balance is consistent and not affected by race conditions.
Keep learning
Related questions
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Choosing a @NotNull Annotation in Java: Validation vs Static Analysis
Learn how Java @NotNull annotations differ, when to use each one, and how to choose between validation, IDE hints, and static analysis tools.
Convert a Java Stack Trace to a String
Learn how to convert a Java exception stack trace to a string using StringWriter and PrintWriter, with examples and common mistakes.