Deep Analysis of Java synchronized Method Lock Mechanism: Object Lock vs Variable-Level Synchronization

Nov 26, 2025 · Programming · 10 views · 7.8

Keywords: Java Synchronization | Object Lock | Multithreading Programming

Abstract: This article provides an in-depth exploration of the lock mechanism in Java synchronized methods, demonstrating through examples that synchronized methods lock the entire object rather than individual variables. When two threads access different synchronized methods of the same object, mutual exclusion occurs even if these methods operate on different variables. The article details three solutions: using synchronized blocks for fine-grained locking, leveraging AtomicInteger atomic classes, and creating independent lock objects, with code examples illustrating each approach's implementation and applicable scenarios.

Principles of Java synchronized Method Lock Mechanism

In Java multithreading programming, the synchronized keyword serves as a crucial mechanism for achieving thread safety. When we use synchronized in method declarations, we essentially add synchronization at the method level. According to the Java language specification, synchronized methods automatically acquire the intrinsic lock of the current object before method invocation and release it after method execution completes.

Empirical Analysis of Object-Level Locking

Consider the following typical example:

class X {
    private int a;
    private int b;

    public synchronized void addA() {
        a++;
    }

    public synchronized void addB() {
        b++;
    }
}

In this example, although addA() and addB() methods operate on different instance variables a and b respectively, they both lock the entire X object instance because they are synchronized methods. This means when two threads simultaneously access addA() and addB() methods of the same X instance, the second thread must wait for the first thread to release the object lock before proceeding.

Underlying Implementation of Lock Mechanism

Synchronized methods are essentially syntactic sugar, with their equivalent form being:

public void addA() {
    synchronized (this) {
        a++;
    }
}

This design ensures that threads must acquire the object's intrinsic lock before accessing its synchronized methods. Every Java object has an associated intrinsic lock (also known as monitor lock), which coordinates multiple threads' access to the object's state.

Fine-Grained Synchronization Solutions

Solution 1: Using synchronized Blocks

To achieve more fine-grained synchronization control, synchronized blocks can be used to lock specific objects:

class X {
    private Integer a = 0;
    private Integer b = 0;

    public void addA() {
        synchronized(a) {
            a++;
        }
    }

    public void addB() {
        synchronized(b) {
            b++;
        }
    }
}

It's important to note that this approach requires the locked objects to be reference types. For primitive data types, since they cannot serve as lock objects, they need to be wrapped into their corresponding wrapper classes first.

Solution 2: Using AtomicInteger Atomic Classes

The Java concurrency package provides atomic classes to address such issues:

import java.util.concurrent.atomic.AtomicInteger;

class X {
    private AtomicInteger a = new AtomicInteger(0);
    private AtomicInteger b = new AtomicInteger(0);

    public void addA() {
        a.incrementAndGet();
    }

    public void addB() {
        b.incrementAndGet();
    }
}

AtomicInteger uses CAS (Compare-And-Swap) operations to achieve thread-safe atomic updates, avoiding explicit lock usage and providing better performance.

Solution 3: Creating Independent Lock Objects

Another common approach is to create dedicated lock objects:

class X {
    private int a = 0;
    private int b = 0;
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void addA() {
        synchronized(lockA) {
            a++;
        }
    }

    public void addB() {
        synchronized(lockB) {
            b++;
        }
    }
}

This method offers maximum flexibility for precise synchronization control but requires careful management of lock objects.

Performance and Concurrency Considerations

When choosing synchronization solutions, it's essential to balance thread safety with performance:

Best Practice Recommendations

In practical development, we recommend following these principles:

  1. Minimize synchronization scope, locking only necessary code segments
  2. Prefer using concurrency utility classes (like AtomicInteger)
  3. Avoid performing time-consuming operations within synchronized blocks
  4. Pay attention to lock granularity to prevent performance issues from over-synchronization
  5. Consider using more modern concurrency mechanisms, such as Lock interface implementations

Conclusion

Java's synchronized methods provide a simple and effective thread synchronization mechanism, but they lock the entire object rather than individual variables. In scenarios requiring higher concurrency performance, finer-grained synchronization strategies should be considered. By appropriately selecting synchronization solutions, we can maximize program concurrency performance while ensuring thread safety.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.