Synchronization in Java

Naveen AnamaneniUpdated · 5 min read

Synchronization in Java is the way to control access to shared data when several threads use it, so that only one thread at a time can run a piece of code that reads or changes that data. Without it, threads can interfere with each other and produce wrong results. Java's basic tool is the synchronized keyword, which uses a lock (also called a monitor) that every object has. In this lesson, you will learn why synchronization is needed, how locks work, and the tools covered in this chapter.

Why Synchronization Is Needed

Threads share the heap. If two threads change the same variable at the same moment, their changes can overlap and one can be lost. This is called a race condition (see "Race Conditions and Thread Safety"). Synchronization prevents it by making a block of code mutually exclusive: while one thread is inside, others must wait.

How a Monitor Works

One thread holds the lock; the others wait outside until it is released
  • Every Java object has one intrinsic lock (monitor).
  • A thread acquires the lock when it enters a synchronized method or block, and releases it when it leaves, even if an exception is thrown.
  • While one thread holds the lock, other threads trying to enter code synchronized on the same object are BLOCKED.
  • Releasing the lock also makes the thread's changes visible to the next thread that takes it.

Example: Fixing a Shared Counter

Let's see an example where two threads add to a counter, first without and then with synchronization. The unsynchronized result changes from run to run, so the output is a sample.

SyncCounter.java▶ Try it
public class SyncCounter {
    static int unsafeCount = 0;
    static int safeCount = 0;
    static final Object lock = new Object();
 
    public static void main(String[] args) throws InterruptedException {
        Runnable work = () -> {
            for (int i = 0; i < 100_000; i++) {
                unsafeCount++;
                synchronized (lock) {
                    safeCount++;
                }
            }
        };
        Thread a = new Thread(work);
        Thread b = new Thread(work);
        a.start();
        b.start();
        a.join();
        b.join();
        System.out.println("Without synchronization: " + unsafeCount);
        System.out.println("With synchronization:    " + safeCount);
    }
}
Output
Without synchronization: 199944
With synchronization:    200000

Explanation: Each thread adds 100,000 to both counters, so both should end at 200,000. unsafeCount++ is really three steps (read, add, write), and the threads' steps overlap, so some updates are lost. (How many varies: in three runs on one machine, 56, 57 and 39 updates were lost.) safeCount++ runs inside synchronized (lock), so only one thread changes it at a time and no update is lost.

Tools for Synchronization

ToolPurposeLesson
synchronized methodsLock the whole method"Synchronized Methods in Java"
synchronized blocksLock only part of a method"Synchronized Block in Java"
wait, notify, notifyAllLet threads signal each other"Inter-Thread Communication in Java"
volatileMake a variable's changes visible"Volatile Keyword in Java"
java.util.concurrent locks, atomics, collectionsMore flexible toolsCovered later in the course

Common Mistakes

Synchronizing on different objects. Threads using different locks don't exclude each other.

Synchronizing only the writes. Reads of shared data need the same lock, or they may see stale values.

Holding a lock while doing slow work such as I/O. Other threads wait unnecessarily.

Interview Corner

What is synchronization in Java?

Synchronization is the mechanism for controlling concurrent access to shared state. With the synchronized keyword, a thread must acquire an object's intrinsic lock (monitor) before executing the guarded method or block, so only one thread at a time can run code guarded by that lock. Releasing the lock on exit, normally or by exception, also publishes the thread's writes to the next thread that acquires the same lock, preventing both race conditions and visibility problems.

Practice

  1. Easy: Run SyncCounter five times and write down the unsafe results.
  2. Harder: Change the example to use four threads and check that the synchronized counter is always 400,000.

Quick check: Synchronization

4 questions
1How many intrinsic locks does a Java object have?
2What state is a thread in while waiting for a synchronized lock?
3Is the lock released if the synchronized code throws?
4Why did unsafeCount lose updates?

Key Takeaways

Was this tutorial helpful?

Lesson 396 of 482