Java concurrency
system-wide lock
Java programming
thread synchronization
Java locks

Getting exclusive system-wide lock in Java

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Standard Java synchronization mechanisms (synchronized, ReentrantLock) only work within a single JVM. For system-wide locking across multiple JVM processes on the same machine, Java provides file-based locking via java.nio.channels.FileLock. For locking across multiple machines, use distributed lock managers like database row locks, Redis (Redisson), or ZooKeeper. The most common approach for single-machine cross-process locking is FileLock on a shared lock file.

In-JVM Locks (Not System-Wide)

java
1// synchronized — works within one JVM only
2public class Counter {
3    private int count = 0;
4
5    public synchronized void increment() {
6        count++;
7    }
8}
9
10// ReentrantLock — also JVM-internal only
11import java.util.concurrent.locks.ReentrantLock;
12
13public class Counter {
14    private final ReentrantLock lock = new ReentrantLock();
15    private int count = 0;
16
17    public void increment() {
18        lock.lock();
19        try {
20            count++;
21        } finally {
22            lock.unlock();
23        }
24    }
25}

These mechanisms coordinate threads within a single process. Two separate Java applications running on the same machine cannot share these locks.

FileLock for System-Wide Locking

java
1import java.io.*;
2import java.nio.channels.*;
3
4public class SystemLock {
5
6    public static void main(String[] args) throws Exception {
7        File lockFile = new File("/tmp/myapp.lock");
8        try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
9
10            // Acquire exclusive lock (blocks until available)
11            FileLock lock = channel.lock();
12            try {
13                System.out.println("Lock acquired — doing critical work");
14                // Only one JVM process can hold this lock at a time
15                Thread.sleep(5000);  // simulate work
16            } finally {
17                lock.release();
18                System.out.println("Lock released");
19            }
20        }
21    }
22}

channel.lock() acquires an exclusive lock on the file. If another process already holds the lock, this call blocks until the lock is released. The OS enforces the lock across all processes.

Non-Blocking tryLock

java
1import java.io.*;
2import java.nio.channels.*;
3
4public class NonBlockingLock {
5
6    public static void main(String[] args) throws Exception {
7        File lockFile = new File("/tmp/myapp.lock");
8        try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
9
10            // tryLock returns null immediately if lock is held by another process
11            FileLock lock = channel.tryLock();
12
13            if (lock == null) {
14                System.out.println("Another instance is already running. Exiting.");
15                System.exit(1);
16            }
17
18            try {
19                System.out.println("Lock acquired — running");
20                Thread.sleep(10000);
21            } finally {
22                lock.release();
23            }
24        }
25    }
26}

tryLock() returns null instead of blocking, which is useful for ensuring only one instance of an application runs at a time.

Reusable Lock Utility

java
1import java.io.*;
2import java.nio.channels.*;
3
4public class SystemWideLock implements AutoCloseable {
5    private final FileChannel channel;
6    private final FileLock lock;
7
8    public SystemWideLock(String lockPath) throws IOException {
9        File file = new File(lockPath);
10        this.channel = new RandomAccessFile(file, "rw").getChannel();
11        this.lock = channel.lock();  // blocks until acquired
12    }
13
14    public static SystemWideLock tryAcquire(String lockPath) throws IOException {
15        File file = new File(lockPath);
16        FileChannel ch = new RandomAccessFile(file, "rw").getChannel();
17        FileLock lk = ch.tryLock();
18        if (lk == null) {
19            ch.close();
20            return null;
21        }
22        SystemWideLock swl = new SystemWideLock();
23        swl.channel = ch;
24        swl.lock = lk;
25        return swl;
26    }
27
28    @Override
29    public void close() throws IOException {
30        try {
31            if (lock != null && lock.isValid()) {
32                lock.release();
33            }
34        } finally {
35            channel.close();
36        }
37    }
38
39    // Private constructor for tryAcquire
40    private FileChannel channelField;
41    private FileLock lockField;
42    private SystemWideLock() {}
43}
44
45// Usage with try-with-resources
46try (SystemWideLock lock = new SystemWideLock("/tmp/myapp.lock")) {
47    // critical section
48}

Server Socket as Single-Instance Lock

java
1import java.net.*;
2
3public class SingleInstance {
4
5    private static ServerSocket lockSocket;
6
7    public static boolean acquireLock(int port) {
8        try {
9            lockSocket = new ServerSocket(port, 0, InetAddress.getByName("127.0.0.1"));
10            return true;
11        } catch (IOException e) {
12            return false;  // port already bound — another instance is running
13        }
14    }
15
16    public static void main(String[] args) {
17        if (!acquireLock(47999)) {
18            System.err.println("Application is already running.");
19            System.exit(1);
20        }
21        System.out.println("Running as single instance...");
22        // application logic
23    }
24}

Binding a ServerSocket to a localhost port is a simple alternative to file locks. If the port is already bound, another instance is running. The OS automatically releases the port when the process exits.

Distributed Locks (Multi-Machine)

java
1// Redis-based distributed lock with Redisson
2import org.redisson.Redisson;
3import org.redisson.api.*;
4import org.redisson.config.Config;
5
6Config config = new Config();
7config.useSingleServer().setAddress("redis://localhost:6379");
8RedissonClient redisson = Redisson.create(config);
9
10RLock lock = redisson.getLock("myapp:critical-section");
11
12lock.lock();  // blocks until acquired across all nodes
13try {
14    // critical section — only one JVM across all machines can execute this
15} finally {
16    lock.unlock();
17}
18
19// With timeout
20boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);
21// wait up to 10s to acquire, auto-release after 30s
java
1// Database-based distributed lock
2// Use SELECT ... FOR UPDATE on a lock row
3try (Connection conn = dataSource.getConnection()) {
4    conn.setAutoCommit(false);
5    try (PreparedStatement ps = conn.prepareStatement(
6            "SELECT * FROM app_locks WHERE lock_name = ? FOR UPDATE")) {
7        ps.setString(1, "critical-section");
8        ps.executeQuery();
9
10        // Lock held — do critical work
11
12        conn.commit();  // releases the row lock
13    }
14}

Common Pitfalls

  • Assuming synchronized works across JVM processes: synchronized and ReentrantLock only coordinate threads within a single JVM. Two separate Java applications cannot share these locks. Use FileLock or a distributed lock for cross-process coordination.
  • Not releasing FileLock in a finally block: If an exception occurs between lock() and release(), the lock is held until the process exits. Always release in a finally block or use try-with-resources.
  • FileLock behavior varies by OS: On Linux, FileLock is advisory — non-Java processes can ignore it. On Windows, it is mandatory (enforced by the OS). Do not rely on FileLock to prevent access from non-cooperating processes on Linux.
  • Forgetting lock expiration in distributed locks: If a process crashes while holding a Redis or database lock, the lock may never be released. Use lock timeouts (TTL) so that stale locks expire automatically.
  • Using file locks on NFS-mounted filesystems: NFS file locking is unreliable and varies by implementation. If processes run on different machines, use a proper distributed lock (Redis, ZooKeeper, database) instead of file locks on shared storage.

Summary

  • synchronized and ReentrantLock work within a single JVM only — not across processes
  • Use java.nio.channels.FileLock for system-wide locking on a single machine
  • channel.lock() blocks until the lock is available; channel.tryLock() returns null immediately if unavailable
  • Binding a ServerSocket to a localhost port is a simple single-instance check
  • For multi-machine distributed locks, use Redis (Redisson), ZooKeeper, or database row locks with SELECT ... FOR UPDATE
  • Always release locks in finally blocks and use timeouts for distributed locks to prevent deadlocks from crashed processes

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.