Synchronization in JavaLast Updated : 20 Jan 2026 Synchronization in Java is a mechanism that controls access to shared resources to prevent data inconsistency in multithreaded programs. In this chapter, you will learn how synchronization works in Java, why it is needed, and how to use synchronized methods and blocks to ensure thread-safe execution. Understanding Threads and Shared ResourcesA thread represents an independent path of execution within a program. When multiple threads access shared resources concurrently, unpredictable interleaving of operations may lead to data inconsistency. Consider a scenario where two threads increment a shared variable at the same time: If two threads execute the increment() method simultaneously, both may read the same value of count, increment it, and write it back. As a result, one update can overwrite the other, leading to incorrect final values. This situation is known as a race condition. Introducing SynchronizationSynchronization addresses such issues by ensuring that only one thread at a time can access a critical section of code or a shared resource associated with an object. This exclusive access prevents race conditions and maintains data consistency. Java provides two primary mechanisms for synchronization:
These mechanisms allow developers to control thread access and ensure safe execution in multithreaded environments. Synchronized MethodsIn Java, you can declare entire methods as synchronized which prevent multiple threads from accessing the method simultaneously. With this, synchronization becomes a simpler process because the mechanism is applied to all invocations of the synchronized method automatically. ExampleIn this example, the SynchronizedCounter class uses synchronized methods to ensure that only one thread at a time can modify or access the shared count variable, preventing race conditions and ensuring thread safety. With this modification, concurrent calls to increment() or getCount() will be synchronized, preventing race conditions. Synchronized BlocksSynchronized block provides exclusive access to shared resources, and only one thread is allowed to execute it in the same time frame. It's structured as follows: This monitor object or lock is the subject. While only one thread can be holding a lock on a monitor object at one instance. Other threads that want to go into the synchronized blocks with this object must wait till the lock becomes available. Intrinsic Locks and SynchronizationIn Java, every object automatically has an intrinsic lock (or monitor lock) associated to it. The moment a thread enters the synchronized block or method, it gets the lock for the object, and then no other thread is allowed to enter the synchronized block or method for that object until the lock is released. 1. DeadlocksOn the one hand, synchronization ensures that race condition is ruled out, but on the other hand, it may lead to deadlocks if not used critically. Stalemates could result as two or more threads are ceaseless when they are waiting for resources from each other. Avoid deadlocks by ordering the locks and releasing them in an opposite sequence of both. 2. Locking GranularityThe best locking granularity has to be selected and has to avoid contention and thus be good for performance. Leaning too broadly can reduce concurrency, while leaning too finely can lead to overhead increase. Identification of the only section of the code which needs to be the only one having an exclusive access to the shared resources and synchronization of that section alone. 3. Concurrent CollectionsJava contains a thread-safe versions of the common collections classes that found in the java.util.concurrent package, including the ConcurrentHashMap and ConcurrentLinkedQueue. These classes provide internal synchronization mechanisms that guarantee the thread safety without giving up the synchronization control to the user. 4. Volatile KeywordFurthermore, volatile keyword may be used to maintain the visibility of changes made to the variables among the threads. For variables declared as volatile, their value will always be read directly from memory and the writes to them will be visible to all the other threads immediately. However, volatile does not ensure as such for complex instructions such as incrementing. 5. Atomic ClassesJava gives atomic classes in the java.util.concurrent.atomic package including Atomic Integer and Atomic Long, which offer atomic operations of variables without using explicit synchronization. These kind of classes use hardware's low level atomic operations to make thread safety. Types of Thread SynchronizationThere are two types of thread synchronization in Java: mutual exclusive and inter-thread communication.
1. Mutual ExclusionMutual exclusion prevents threads from interfering with one another while accessing shared data. It ensures that only one thread can execute a critical section at a time. In Java, mutual exclusion can be achieved in the following ways:
Concept of LockSynchronization is built around an internal entity called a lock or monitor. Every object in Java has an associated lock. When a thread enters a synchronized method or block:
Other threads must wait until the lock is released before accessing the synchronized code. From Java 5 onward, the java.util.concurrent.locks package provides advanced lock implementations for more flexible synchronization. Understanding the Problem without SynchronizationWhen synchronization is not used, multiple threads can access shared resources simultaneously, leading to unpredictable output. Consider the following example where the method is not synchronized: Output: 5 100 10 200 15 300 20 400 25 500 The output is inconsistent because both threads execute the method simultaneously. 2. Cooperation (Inter-thread Communication)Inter-thread communication allows multiple threads to coordinate their execution by communicating with each other instead of competing for a resource. This cooperation is achieved using the wait(), notify(), and notifyAll() methods of the Object class. Consider a restaurant scenario, where the Cook thread prepares the food, the Customer thread waits until the food is ready, and once the food is prepared, the cook notifies the customer to proceed. In the below example code, here one thread waits for a condition to be fulfilled while another thread performs an action and notifies the waiting thread to continue execution. ExampleOutput: Customer is waiting for food... Cook is preparing food... Customer is served food. Next TopicJava-synchronized-block |
We request you to subscribe our newsletter for upcoming updates.