Java Synchronization
In this page:
synchronized returnType methodName() {
// one thread at a time
}
synchronized (lockObject) {
// critical section
}
What is Synchronization?
Synchronization is the mechanism Java provides to control how multiple threads access shared resources, preventing race conditions, data corruption, and subtle memory-visibility bugs. Without it, two threads reading and writing the same variable at once can produce results that neither thread individually intended.
Example: What is Synchronization?
public class Main {
static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Runnable increment = () -> { for (int i = 0; i < 1000; i++) counter++; };
Thread t1 = new Thread(increment);
Thread t2 = new Thread(increment);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(counter); // may be less than 2000 without synchronization
}
}
Login to try C/C++/Java/PHP code in the editor
Synchronized Methods
By adding the synchronized keyword to a method's declaration, you restrict access so that only one thread at a time can execute that method on a given object instance. Any other thread that tries to call it must wait until the first thread exits the method, whether normally or via an exception.
Example: Synchronized Methods
public class Main {
static class Counter {
private int count = 0;
synchronized void increment() { count++; }
int getCount() { return count; }
}
public static void main(String[] args) throws InterruptedException {
Counter c = new Counter();
Runnable task = () -> { for (int i = 0; i < 1000; i++) c.increment(); };
Thread t1 = new Thread(task), t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(c.getCount()); // reliably 2000
}
}
Login to try C/C++/Java/PHP code in the editor
Synchronized Blocks
Synchronizing an entire method can create a performance bottleneck if most of the method's work doesn't actually touch shared state. Synchronized blocks let you lock only the specific lines that need protection, shrinking the critical section and reducing how long other threads have to wait.
Example: Synchronized Blocks
// Define the class Main
public class Main {
static int total = 0;
// Create a new Object object and store it in lock
static final Object lock = new Object();
// Program entry point: the JVM starts running here
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
// Loop: repeat while the condition holds
for (int i = 0; i < 1000; i++) {
// synchronized lets only one thread run this code at a time
synchronized (lock) { total++; }
}
};
// Create a new Thread object and store it in t1
Thread t1 = new Thread(task), t2 = new Thread(task);
// Start the thread; it runs in parallel with this code
t1.start(); t2.start();
// Wait for the thread to finish
t1.join(); t2.join();
System.out.println(total);
}
}
Login to try C/C++/Java/PHP code in the editor
Static Synchronization
If you declare a static method as synchronized, the lock acquired is on the Class object itself rather than on any individual instance. This is what actually protects static, class-level variables shared across every instance, since instance-level locks wouldn't cover them.
Example: Static Synchronization
public class Main {
static int sharedCount = 0;
static synchronized void increment() { // locks the Class object, not an instance
sharedCount++;
}
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> { for (int i = 0; i < 1000; i++) increment(); };
Thread t1 = new Thread(task), t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(sharedCount);
}
}
Login to try C/C++/Java/PHP code in the editor
Understanding Deadlocks
A deadlock occurs when two or more threads are permanently blocked, each one waiting on a lock that another blocked thread is holding, so neither can ever proceed. Acquiring locks in a consistent, predictable order across your codebase, and avoiding nested synchronized blocks where possible, is the standard way to prevent this.
Example: Understanding Deadlocks
public class Main {
static final Object lockA = new Object();
static final Object lockB = new Object();
public static void main(String[] args) {
// Acquiring locks in a consistent order (lockA then lockB) avoids deadlock
synchronized (lockA) {
synchronized (lockB) {
System.out.println("Both locks acquired safely, in order");
}
}
}
}
Login to try C/C++/Java/PHP code in the editor
- Assuming
counter++is atomic, when it is read, add and write, so two threads can lose updates withoutsynchronizedor an atomic class. - Synchronizing on different objects in different threads, such as
synchronized (new Object()), so no lock is actually shared and nothing is protected. - Locking two objects in different orders in different threads, which can cause a deadlock where each thread waits forever for the other's lock.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: