In multithreaded programming, multiple threads may try to read and modify shared data at the same time, leading to data corruption, inconsistent results, or unexpected behavior. To solve this, Java provides a built-in mechanism called synchronization to control access to critical sections of code.
Java offers two primary ways to synchronize code:
synchronized
methodssynchronized
blocks
Both are used to ensure that only one thread at a time executes a specific section of code that accesses shared resources. But they offer different levels of control and performance.
1. What is a synchronized
Block?
A synchronized block in Java is a section of code enclosed in a synchronized
statement that locks a specific object explicitly. It allows you to synchronize only a portion of the code (a critical section), rather than the entire method.
1.1 Syntax
synchronized (lockObject) { // critical section }
Here:
lockObject
is any non-null object used as a monitor (lock).- When a thread enters the synchronized block, it acquires the monitor lock on that object.
- Other threads trying to enter any synchronized block or method using the same object will wait until the lock is released.
1.2 Example: Using synchronized
block
public class Counter { private int count = 0; private final Object lock = new Object(); public void increment() { // Only this critical section is synchronized synchronized (lock) { count++; } } public int getCount() { return count; } }
In this example:
- Only the increment operation is synchronized, making it thread-safe.
getCount()
is not synchronized, allowing non-blocking reads.lock
is a private object used for locking. This provides better encapsulation and prevents other classes from synchronizing on it.
2. What is a synchronized
Method?
A synchronized method is a method that has the synchronized
keyword in its declaration. When a thread calls a synchronized method, it automatically acquires a lock on the object (for instance methods) or on the class object (for static methods).
2.1 Syntax
public synchronized void increment() { count++; }
This is equivalent to:
public void increment() { synchronized (this) { count++; } }
In the case of a static synchronized
method, the lock is on the class’s Class object, not on the instance.
public static synchronized void staticIncrement() { // lock is on ClassName.class }
2.2 Example: Using synchronized
method
public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
Here:
- Entire method is synchronized.
- Every time a thread calls
increment()
orgetCount()
, it must acquire the lock on the current instance (this
). - This can lead to unnecessary blocking if only a small part of the method modifies shared state.
3. Differences: synchronized
Block vs synchronized
Method
Let’s compare the two approaches in detail:
Feature | synchronized Method | synchronized Block |
---|---|---|
Granularity | Locks the entire method, even if only a small part needs protection. | Locks only the specific block of code that accesses shared resource. |
Lock Object | Implicit: instance method → this , static method → class (ClassName.class ). | Explicit: you can choose any object as the lock. |
Performance | May result in longer lock duration, potentially reducing concurrency. | More fine-grained control, can improve performance by locking only critical sections. |
Readability | Easier to understand at first glance. | Slightly more complex but more flexible. |
Flexibility | Cannot change the lock object (always this or ClassName.class ). | You can synchronize on any dedicated lock object, improving encapsulation. |
Use Case | When entire method must be thread-safe. | When only a small section of the method needs thread safety. |
4. Java Memory Model and synchronized
synchronized
ensures visibility and atomicity:- Changes made by one thread to shared variables inside a synchronized block are visible to other threads.
- Synchronization prevents instruction reordering and enforces happens-before relationships.
5. Alternatives to synchronized
ReentrantLock
(fromjava.util.concurrent.locks
) — more powerful, allows try-lock, interruptible lock, fairness, etc.AtomicInteger
,AtomicReference
— lock-free synchronization for some use cases.ConcurrentHashMap
,CopyOnWriteArrayList
— for concurrent data structures.