"Synchronized" in Java: What It Is and How It Works with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

"What does synchronized mean?" — a question that haunts every other junior

\n\n

If you've ever written a multithreaded application in Java, you've likely encountered this situation: two threads simultaneously modify the same variable, resulting in bugs that reproduce once in a hundred runs. Sound familiar? This is exactly what the synchronized keyword is for. But what does it actually do? How does it work "under the hood"? And most importantly — when should you use it, and when is it better to avoid it?

\n\n

In this article, we won't just paraphrase the documentation. We'll break down a popular Stack Overflow question (the one that gathered thousands of views), adapt it into an educational format, and show a comparison of approaches: synchronization at the method level, at the block level, and alternatives from java.util.concurrent. Let's go!

\n\n

The Problem: Race Condition

\n\n

Imagine: you have a counter that two threads increment. The code is simple, but the result is unexpected.

\n\n
public class Counter {\n    private int count = 0;\n\n    public void increment() {\n        count++;\n    }\n\n    public int getCount() {\n        return count;\n    }\n}
\n\n

We run two threads, each performing 1000 increments. You'd expect 2000. But in reality — 1987, 1993, or even 1765. Why? Because the count++ operation is not atomic. It consists of three steps: read → increment → write. The threads "overlap," and one change gets lost.

\n\n

This is a race condition. And synchronized is one of the main tools to combat it.

\n\n

What does synchronized do? Let's look under the hood

\n\n

The synchronized keyword in Java guarantees mutual exclusion (mutex) and visibility of changes between threads. When a thread enters a synchronized block, it acquires the monitor of the object (or class). Other threads trying to enter any synchronized block on the same monitor are blocked until it is released.

\n\n

Important: synchronized only works with one monitor. If you have two different objects — synchronization will not work.

\n\n

Two levels of synchronization

\n
    \n
  • Synchronized method — locks the entire method, uses the object's monitor (this).
  • \n
  • Synchronized block — locks only part of the code, you can specify any object as the monitor.
  • \n
\n\n

Let's compare them in practice.

\n\n

Comparison of approaches: method vs block

\n\n

1. Synchronized method

\n\n

The simplest way — add synchronized to the method signature.

\n\n
public class SafeCounter {\n    private int count = 0;\n\n    public synchronized void increment() {\n        count++;\n    }\n\n    public synchronized int getCount() {\n        return count;\n    }\n}
\n\n

Pros:

\n
    \n
  • Minimal code, easy to read.
  • \n
  • Guarantees the entire method is an atomic operation.
  • \n
\n\n

Cons:

\n
    \n
  • "Heavy" locking — even if the method has only one line, the thread is blocked for the entire execution time.
  • \n
  • Reduces performance if the method is long or called frequently.
  • \n
  • Not flexible — you cannot synchronize only part of the logic.
  • \n
\n\n

2. Synchronized block

\n\n

More fine-grained control. You decide which object to use as the monitor.

\n\n
public class SafeCounterWithBlock {\n    private int count = 0;\n    private final Object lock = new Object();\n\n    public void increment() {\n        synchronized (lock) {\n            count++;\n        }\n    }\n\n    public int getCount() {\n        synchronized (lock) {\n            return count;\n        }\n    }\n}

Blogs

Book Recommendations