Skip to main content
Documents
Share
LinkedIn
Facebook
Twitter
Copy
Java keywords

synchronized Keyword in Java

The synchronized keyword in Java controls access to shared resources among multiple threads. It guarantees that only one thread can executed a synchronized block or method at a time, preventing thread interference and ensuring memory consistency.

Usage

The synchronized keyword can be applied to methods or blocks within methods. When a method or block is declared as synchronized, a lock is obtained on the specified object, and other threads are blocked from accessing the synchronized code until the lock is released.

Syntax

Synchronized Method

public synchronized void synchronizedMethod() {
    // method code
}

Synchronized Block

public void method() {
    synchronized (object) {
        // synchronized code
    }
}
  • object: The object on which to synchronize. This is typically the current instance (this) or a specific object.

Examples

Example 1: Synchronized Method

public class Counter {
    private int count = 0;

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

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();

        // Create multiple threads to increment the counter
        Thread t1 = new Thread(() -> counter.increment());
        Thread t2 = new Thread(() -> counter.increment());

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + counter.getCount());
    }
}

In this example, the increment method is synchronized, ensuring that only one thread can increment the counter at a time, preventing race conditions.

Example 2: Synchronized Block

public class SynchronizedBlockExample {
    private final Object lock = new Object();
    private int count = 0;

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        SynchronizedBlockExample example = new SynchronizedBlockExample();

        // Create multiple threads to increment the counter
        Thread t1 = new Thread(() -> example.increment());
        Thread t2 = new Thread(() -> example.increment());

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + example.getCount());
    }
}

This example uses a synchronized block to control access to the count variable. The lock object ensures that only one thread can execute the synchronized block at a time.

Tips and Best Practices

  • Minimize Synchronized Code: Keep synchronized blocks as short as possible to reduce contention and improve performance.
  • Use Final Objects: When using synchronized blocks, use final objects as locks to ensure consistency.
  • Avoid Nested Synchronization: Nested synchronized blocks can lead to deadlocks. Avoid them or use them with caution.
  • Use Concurrent Collections: For complex synchronization, consider using concurrent collections from the java.util.concurrent package, such as ConcurrentHashMap, which provide built-in synchronization.
  • Volatile Keyword: For variables that are accessed by multiple threads but do not require complex synchronization, consider using the volatile keyword to ensure visibility.
private volatile boolean flag;
  • Thread Safety: Always ensure that shared resources are properly synchronized to maintain thread safety and avoid data corruption.
  • Synchronized Static Methods: Synchronize static methods to enforce synchronized access at the class level, ensuring only one thread can execute the static method across all instances.
public static synchronized void staticMethod() {
    // method code
}
  • Reentrancy: Java's intrinsic locks are reentrant, meaning if a thread holds a lock, it can acquire it again without deadlocking itself.