How to Use Redis Locks in Java

In programming and computer science terms, locks control access to shared resources, preventing multiple threads from accessing or modifying data simultaneously. Locks ensure data consistency and prevent unexpected data loss in nearly every application. 

For example, when a user opens a spreadsheet document from a shared network volume, the file server locks it so no other users can write to it, ensuring data consistency. Database servers routinely lock tables when records are updated, keeping other processes from writing to the locked tables.

The popular in-memory data store Redis and its open-source offshoot Valkey have their own locking mechanism, officially known as distributed locking. Java developers can use the Redisson client to simplify the creation and management of Valkey and Redis locks via simple and familiar Java objects.

About Java Lock Objects and Distributed Locking

Developers implement locking mechanisms to ensure that only one thread can access a specific resource at any given time. In most implementations, a thread must acquire the lock before accessing the resource, perform its operations, and then release the lock, allowing other threads to access it.

In Java, Lock objects offer substantial flexibility since they can be used across multiple threads. With Lock, a thread only acquires the lock if it’s immediately available, reducing wait times. In addition, Lock allows for thread interruption during waiting times.

With distributed systems, such as high-performance application clusters, multiple threads run simultaneously on different servers. This is where the concept of distributed locking comes in. Distributed locking ensures that only one thread across all servers or instances can access a shared resource at any given time. Distributed locking—such as the Valkey or Redis locks implementation—provides coordination across these multiple servers or application instances. 

Redisson simplifies the use of Valkey or Redis locks in Java so that distributed locking is no more complicated than the standard Java Lock object. Here’s a look at Redisson’s implementations of Redis locks.

Lock

Redisson’s RLock object implements a reentrant lock, which simply means that threads can lock a resource more than once. A counter variable keeps track of the number of lock requests. Once enough unlock requests have brought the counter to 0, the resource is released. Here’s a RLock code sample with a basic lock:

RLock lock = redisson.getLock("myLock");
// traditional lock method
lock.lock();

// or acquire lock and automatically unlock it after 10 seconds
lock.lock(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
    try {
    	
    	//...
    	
    } finally {
    	lock.unlock();
    }
}

Fair Lock

Redisson also supports Redis fair locks, which guarantee that threads will acquire a resource in the order they requested it — the “first in, first out” principle. Here’s how to implement a fair lock with the getFairLock method:

RLock lock = redisson.getFairLock("myLock");

// traditional lock method
lock.lock();

// or acquire lock and automatically unlock it after 10 seconds
lock.lock(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
    try {

       //...

    } finally {
       lock.unlock();
    }
}

MultiLock

Redisson allows Java developers to group Lock objects and manage them as a single lock with its MultiLock object. You simply define a few RLock objects and then group them together with multiLock, like this:

RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");

RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);

// traditional lock method
multiLock.lock();

// or acquire lock and automatically unlock it after 10 seconds
multiLock.lock(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {

      //...
  
   } finally {
      multiLock.unlock();
   }
}

ReadWriteLock

Java has the concept of read/write locks, which are actually a combination of two locks. The first is a read-only lock that multiple threads can own. The second is a write lock that can only be owned by a single thread at a time. Redisson’s RReadWriteLock implements this for Redis locks. Here’s how Java developers can use getReadWriteLock:

RReadWriteLock rwlock = redisson.getReadWriteLock("myLock");

RLock lock = rwlock.readLock();
// or
RLock lock = rwlock.writeLock();

// traditional lock method
lock.lock();

// or acquire lock and automatically unlock it after 10 seconds
lock.lock(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {

      //...

   } finally {
      lock.unlock();
   }
}

Spin Lock

In some applications, thousands or more locks may be acquired or released in a short time. This may cause a surge in network traffic, potentially reaching the network’s limits or overloading a Redis or Valkey server. Redisson’s Spin Lock uses an exponential backoff strategy to help avoid overloading. Here’s how to use the getSpinLock method:

RLock lock = redisson.getSpinLock("myLock");

// traditional lock method
lock.lock();

// or acquire lock and automatically unlock it after 10 seconds
lock.lock(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
    try {

       //...
   
    } finally {
       lock.unlock();
    }
}

Fenced Lock

In some cases, a client that acquires a lock is delayed due to a long pause, or it may not be able to detect that it doesn’t own a lock anymore. Redisson helps resolve this issue by maintaining the fencing token. This mechanism is known as a Fenced Lock. Here’s how to implement the getFencedLock method:

RFencedLock lock = redisson.getFencedLock("myLock");

// traditional lock method
Long token = lock.lockAndGetToken();

// or acquire lock and automatically unlock it after 10 seconds
token = lock.lockAndGetToken(10, TimeUnit.SECONDS);

// or wait for lock acquisition up to 100 seconds 
// and automatically unlock it after 10 seconds
Long token = lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS);
if (token != null) {
    try {

       // check if token >= old token
       //...

    } finally {
       lock.unlock();
    }
}

Using Redisson to Manage Redis Locks

While the lock is a basic concept in programming, working with locks becomes exponentially more complicated in distributed applications. Distributed locking can be extremely challenging to manage in code, but Redisson makes it easy to manage Redis locks with methods that will be familiar to any Java developer. From basic locks to more advanced implementations like Spin Locks and Fenced Locks, Redisson simplifies Redis locks. To learn more, visit the Redisson PRO website today.

Similar articles