Backend Development 8 min read

Why Java synchronized Is Insufficient in Distributed Systems and Alternative Lock Solutions

The article explains how Java's synchronized keyword works only within a single JVM, why it fails to provide mutual exclusion across multiple processes in distributed systems, and presents alternative distributed locking mechanisms such as database locks and Redis-based locks with code examples.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Why Java synchronized Is Insufficient in Distributed Systems and Alternative Lock Solutions

Conclusion: In distributed systems, using Java's synchronized keyword alone cannot meet synchronization requirements.

1. How synchronized works

In Java, synchronized is used for thread synchronization within a single JVM, ensuring mutual exclusion by acquiring the object's monitor. It only works inside one JVM process.

Example code:

public class SynchronizedExample {
    private int count = 0;

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

    public static void main(String[] args) throws InterruptedException {
        SynchronizedExample example = new SynchronizedExample();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Count: " + example.count);
    }
}

This example shows the increment method protected by synchronized, preventing race conditions.

2. Limitations in distributed scenarios

Distributed systems consist of multiple independent JVM processes that cannot share monitors. synchronized only guarantees synchronization within a single JVM and cannot provide cross‑process mutual exclusion, leading to possible data inconsistency.

3. Alternative solutions for distributed locking

1. Database lock

Use row‑level or table‑level locks, e.g., MySQL SELECT ... FOR UPDATE.

-- Acquire row lock
SELECT * FROM distributed_lock_table WHERE lock_name = 'resource_lock' FOR UPDATE;

Simple to implement but performance may be poor and heavily depends on the database.

2. Redis distributed lock – SETNX

Redis provides atomic operations; a common implementation uses the SETNX command.

import redis.clients.jedis.Jedis;

public class RedisDistributedLock {
    private static final String LOCK_KEY = "distributed_lock";
    private static final String LOCK_VALUE = "lock_value";
    private static final int EXPIRE_TIME = 1000; // ms

    public static boolean acquireLock(Jedis jedis) {
        String result = jedis.set(LOCK_KEY, LOCK_VALUE, "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }

    public static void releaseLock(Jedis jedis) {
        jedis.del(LOCK_KEY);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        if (acquireLock(jedis)) {
            try {
                System.out.println("Lock acquired, executing critical section");
            } finally {
                releaseLock(jedis);
            }
        } else {
            System.out.println("Failed to acquire lock");
        }
        jedis.close();
    }
}

High performance but requires a Redis service and handling of expiration and failures.

3. Redis distributed lock – TryLock

Implemented using SET with NX and PX options to atomically set the lock and its expiration.

import redis.clients.jedis.Jedis;

public class RedisTryLockExample {
    private static final String LOCK_KEY = "distributed_lock";
    private static final String LOCK_VALUE = "lock_value";
    private static final int EXPIRE_TIME = 10000; // ms
    private Jedis jedis;

    public RedisTryLockExample() {
        this.jedis = new Jedis("localhost", 6379);
    }

    public boolean tryLock() {
        String result = jedis.set(LOCK_KEY, LOCK_VALUE, "NX", "PX", EXPIRE_TIME);
        return "OK".equals(result);
    }

    public void unlock() {
        jedis.del(LOCK_KEY);
    }

    public static void main(String[] args) {
        RedisTryLockExample lockExample = new RedisTryLockExample();
        if (lockExample.tryLock()) {
            try {
                System.out.println("Lock acquired, executing critical section");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lockExample.unlock();
                System.out.println("Lock released");
            }
        } else {
            System.out.println("Failed to acquire lock");
        }
        lockExample.jedis.close();
    }
}

SETNX lacks atomic expiration, which can cause deadlocks; TryLock solves this by setting expiration atomically.

4. Summary

In distributed systems, synchronized cannot be used directly for synchronization; appropriate distributed lock mechanisms such as database locks or Redis locks should be chosen based on the specific scenario.

Javaconcurrencyredisdistributed locksynchronizeddatabase lock
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.