Java’s Synchronized Keyword in Three Minutes

Share this article

Java’s Synchronized Keyword in Three Minutes

The synchronized keyword can be used to ensure that only one thread at a time executes a particular section of code. This is a simple way to prevent race conditions, which occur when several threads change shared data at the same time in a way that leads to incorrect results. With synchronized either entire methods or selected blocks can be single-threaded.

This article requires basic knowledge of Java threads and race conditions.

Synchronization Basics

Let’s see how to use synchronized on methods and, more fine-grained, on code blocks. I will also explain how it works.

Using The synchronized Keyword

If we add a synchronized modifier to a method, the JVM guarantees that only one thread can execute a method at the same time:

class MutableInteger {

    private int value;

    public synchronized void increment() {
        value++;
    }

    public synchronized int getValue() {
        return value;
    }

}

If several threads try to execute a synchronized method simultaneously, only one will be able to do it. The others will be paused until the first one exits the method. (In some sense it works much like a traffic light, making sure concurrent executions don’t collide.) Because of this, threads that increment the value counter do not overwrite each other’s results and every increment will be recorded.

To see that the synchronized keyword works I create ten threads that each increment the same counter ten thousand times. I created a gist with the code – it is basically the same as in the article explaining race conditions. As expected, the result is always the same and always correct:

Result value: 100000

The synchronized keyword only limits thread access to one object. If you have two different instances, two different threads can use them at the same time, and they won’t block each other:

MutableInteger integer1 = new MutableInteger();
MutableInteger integer2 = new MutableInteger();
// Threads are using different objects and don't
// interact with each other
Thread thread1 = new Thread(new IncrementingRunnable(integer1));
Thread thread2 = new Thread(new IncrementingRunnable(integer2));

How synchronized Works

Synchronization has two forms: synchronized methods and synchronized statements. So far we’ve only encountered the first type, here’s the second:

class MutableInteger {

    private int value;

    private Object lock = new Object();

    public void increment() {
        // Only one thread can execute this block of code at any time
        synchronized (lock) {
            value++;
        }
    }

    public int getValue() {
        // Only one thread can execute this block of code at any time
        synchronized (lock) {
            return value;
        }
    }

}

When a thread enters a synchronized block, it attempts to acquire the lock that is associated with the object passed to the statement. (In Java, an object’s lock is often called its monitor.) If another thread is currently holding the lock, the current one will be paused until the lock is released. Otherwise the thread succeeds and enters the synchronized block.

When the thread finishes the synchronized block, it releases the lock, and another thread can acquire it. A lock is released when a thread leaves a synchronized block even if an exception is thrown.

By the way, using the synchronized keyword on a method instead of in a block is just shorthand for using the object’s own lock for synchronization:

// this is equivalent to 'public synchronized void ...'
public void increment() {
    synchronized (this) {
        value++;
    }
}

Maybe you are now wondering what lock is used if you synchronize a static method.

public static synchronized void foo() { ... }

In that case it is the class object itself that get’s locked on, so it is equivalent to the following:

public static void foo() {
    synchronized (TheClassContainingThisMethod.class) {
        ...
    }
}

Multiple Locks

So far we’ve seen that a class only uses a single lock object, but it is common to use multiple locks in one class. This allows two different threads to execute two different methods on a single object in parallel:

class TwoCounters {

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    private int counter1;
    private int counter2;

    public void incrementFirst() {
        synchronized (lock1) {
            counter1++;
        }
    }

    public void incrementSecond() {
        synchronized (lock2) {
            counter2++;
        }
    }

}

Counting in a real-world Java application

Synchronization is commonly used in real-world Java applications and is a powerful tool, but I would not advise you to use it for aggregating values from different threads as we did here. Java provides a special class for this, called AtomicInteger, as a part of its rich concurrency package. It has better performance than the MutableInteger that we’ve implemented here and a much richer interface.

"The synchronized keyword works much like a traffic light."

Conclusions

In this post, you’ve learned how to avoid race conditions with the synchronized keyword, which guarantees that only one thread con execute a given block of code. It can be used on a method level, in which case it uses the object itself as a lock, or on a block level, in which case the lock object has to be specified. Any Java object can be used as a lock.

A similarly fundamental concurrency feature are the Object methods wait and notify, which can be used to implement guarded blocks to coordinate actions between different threads. Beyond that, Java has the Lock interface that allows implementing synchronization similarly to the synchronized keyword but has more flexibility. If you want to use collections in multiple threads, have a look at synchronized wrappers and particularly concurrent collections.

Frequently Asked Questions (FAQs) about Java Synchronized Keyword

What is the main purpose of the synchronized keyword in Java?

The synchronized keyword in Java is primarily used to achieve thread-safety. It ensures that only one thread can access a method or block at a time, preventing data inconsistency and race conditions in multi-threaded environments. This is particularly useful when you have critical sections of code that should not be executed by more than one thread simultaneously.

How does the synchronized keyword work in Java?

When a thread encounters a method or block marked with the synchronized keyword, it needs to acquire a lock before it can enter. This lock is released when the thread exits the synchronized method or block, allowing other threads to enter. If another thread tries to enter a synchronized method or block while a thread holds the lock, it will be blocked until the lock is released.

Can you explain the difference between synchronized methods and synchronized blocks in Java?

Synchronized methods and synchronized blocks in Java both serve the purpose of controlling access to critical sections of code in a multi-threaded environment. The main difference lies in their scope. A synchronized method locks the entire method, while a synchronized block only locks the specific part of the method where it’s applied. This means that synchronized blocks often provide more granular control over synchronization and can lead to better performance in some cases.

What is the role of the ‘this’ keyword in synchronized blocks?

In synchronized blocks, the ‘this’ keyword is used to denote the current object. When ‘this’ is used as the argument in a synchronized block, the lock is on the current object. This means that all synchronized blocks of the same object can only be accessed by one thread at a time.

What is reentrant synchronization in Java?

Reentrant synchronization in Java refers to the ability of a thread that has acquired a lock to enter a synchronized block of code again using the same lock. This is possible because Java’s synchronization is reentrant in nature. The thread can acquire the same lock again without getting blocked, preventing situations like deadlock.

What is the difference between synchronized and volatile keywords in Java?

Both synchronized and volatile keywords in Java are used for achieving thread-safety, but they serve different purposes. The synchronized keyword is used to control access to blocks of code or methods, ensuring that only one thread can execute the code at a time. On the other hand, the volatile keyword is used to indicate that a variable’s value can be modified by different threads and ensures that all reads and writes to the variable are directly from and to the main memory.

Can we use the synchronized keyword with constructors in Java?

No, we cannot use the synchronized keyword with constructors in Java. This is because constructors are inherently thread-safe. Only one thread can create an object at a time, and until the object is fully created, it’s not visible to other threads.

What is intrinsic lock or monitor lock in Java?

Intrinsic lock or monitor lock in Java is a lock that every object has. When a thread enters a synchronized method or block, it acquires the intrinsic lock on that object. Other threads attempting to enter the synchronized method or block are blocked until the thread currently in the method or block releases the lock.

What is the impact of the synchronized keyword on performance?

The synchronized keyword can have a significant impact on performance. It can cause thread contention, which happens when multiple threads try to acquire a lock on the same resource simultaneously, leading to thread blocking. This can slow down the application, especially in high concurrency scenarios. Therefore, it’s important to use synchronization judiciously.

Can we use the synchronized keyword with static methods?

Yes, we can use the synchronized keyword with static methods. When a static method is synchronized, the thread acquires a lock on the class object, not on the instance of the class. This means that all synchronized static methods of the class can only be accessed by one thread at a time.

Ivan MushketykIvan Mushketyk
View Author

Passionate software developer interested in Java, Scala, and Big Data. Apache Flink contributor. Live in Dublin, Ireland.

concurrencynicolaipquick-tiprace conditionsynchronized
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week