Redis based distributed lock with Redisson in spring boot

Serdar A.
4 min readMar 13, 2025

--

Distributed locking is a technique to manage many applications that try to access the same resource. The main purpose is to allow only one of many applications to access the same resource at the same time.

Distributed locks are useful in any situation in which multiple systems may operate on the same state concurrently.To ensure that no two threads have simultaneous access to the same resource and that the resource is operated upon in a predictable sequence, programmers use a mechanism known as locks. Each thread first acquires the lock, operates on the resource, and finally releases the lock to other threads.

Redisson has many distributed Java collections, objects, and service implementations. With these features, Redisson significantly reduces the Redis learning curve for Java developers. In particular, Redisson makes it easier than ever to build key-value Redis databases.The Redisson framework is a Redis-based In-Memory Data Grid for Java that provides multiple objects for programmers who need to perform distributed locking.

Let’s coding scenario

I want to use locking in two scenarios.

  • First scenario is that using locking and unlocking on same thread or API.
  • Other scenario is that using locking and unlocing on different API
  1. Firstly add radisson dependency
  <dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.45.0</version>
</dependency>

2) Redisson Client Configuration

 @Configuration
@RequiredArgsConstructor
public class RadissonConfig {

private final RedisPropConfig redisPropConfig;

@Bean
public RedissonClient redisson() {

Config config = new Config();
config.useSingleServer().setAddress("redis://" + "hostInfo" + ":" + "portInfo");
return Redisson.create(config);

}
}

You can change host and port info according to your system.

Note : You can look up other configuration for Radisson :

3) If you want to lock on same API

public static final String LOCK_PREFIX_KEY = "sys_lock_key:";

public ResponseEntity<String> redissonTest(String parameter) {


String lockKey = LOCK_PREFIX_KEY + parameter;

RLock lock = redissonClient.getLock(lockKey);


try {
boolean acquired = lock.tryLock(500L, 5000L, TimeUnit.MILLISECONDS);

if (acquired) {

try
{

// bussiness code
// if you want to add Thread.sleep(3000)

new ResponseEntity<>(xxxx, OK);
}

finally
{
if(lock.isLocked() && lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}

}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Request processing was interrupted");

}

}

lockKey : Your lock key with in which you want to lock data.

getLock(lockKey) : Returns Lock instance by lockKey.

tryLock : Returns true as soon as the lock is acquired.

  • First parameter is waitTime. This means that waiting for lock aquisition up to 500 miliseconds.
  • Second parameter is leaseTime. This means that during lock acquisition can be defined. After specified time interval locked lock will be released automatically.Automatically unlock it after 5000L miliseconds.
  • Third parameter is timeunit. You select MiliSecond, Second etc.

acquired : If lock is true after calling tryLock process. acquired is true. If other threads access this block or resource with same lockKey, these threads waits. When unlocking by thread which adds lock, another thread accesses this block.

isLocked & isHeldByCurrentThread : Before call lock.unlock(), you can check lock or if locking held by current thread.

unlock : Lock process can be unlocked. You can write unlock code on finally block or catch block.

4) You can handle timeout

public static final String LOCK_PREFIX_KEY = "sys_lock_key";

private static final long MAX_WAIT_TIME = 2 * 60 * 1000;


public ResponseEntity<String> redissonTest(String parameter) {


String lockKey = LOCK_PREFIX_KEY + parameter;

RLock lock = redissonClient.getLock(lockKey);

long startTime = System.currentTimeMillis();

while (true) {

try {

boolean acquired = lock.tryLock(500L, 5000L, TimeUnit.MILLISECONDS);

if (acquired) {

try
{

// bussiness code
// if you want to add Thread.sleep(3000)

new ResponseEntity<>(xxxx, OK);
}

finally
{
if(lock.isLocked() && lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}

if (System.currentTimeMillis() - startTime > MAX_WAIT_TIME) {

return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Request timed out waiting to process key: " + lockKey);
}

}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Request processing was interrupted");
}

}

}

You can check timeout duration unless acquired is true on below code block and return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)

if (System.currentTimeMillis() - startTime > MAX_WAIT_TIME) {

return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Request timed out waiting to process key: " + lockKey);
}

5) Using locking and unlocking on different API

public interface LockService {

public boolean acquireLock(String lockName, long waitTime, long leaseTime);

public void releaseLock(String lockName);

public void forceReleaseLock(String lockName);

public boolean isLocked(String lockName);
}
@Service
public class LockServiceImpl implements LockService {

private final RedissonClient redissonClient;

@Autowired
public LockServiceImpl(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}

@Override
public boolean acquireLock(String lockName, long waitTime, long leaseTime) {

RLock lock = redissonClient.getLock(lockName);

try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}

@Override
public void releaseLock(String lockName) {
RLock lock = redissonClient.getLock(lockName);
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}

@Override
public void forceReleaseLock(String lockName) {
RLock lock = redissonClient.getLock(lockName);
if (lock.isLocked()) {
lock.forceUnlock();
}
}

@Override
public boolean isLocked(String lockName) {
RLock lock = redissonClient.getLock(lockName);
return lock.isLocked();
}
}
// other code blocks

public static final String LOCK_PREFIX_KEY = "sys_lock_key";

@Autowired
private final LockService lockService;


public ResponseEntity<String> startProcessAPI(Long id, String otherParameter) {
{
String lockName = LOCK_PREFIX_KEY + id;

boolean isLock = lockService.acquireLock(lockName,500L,5000L);

if (isLock)
{
//other process
}
else
{
return new ResponseEntity<>(HttpStatus.LOCKED);
}
}


public ResponseEntity<String> endProcessAPI(Long id, String otherParameter) {
{
String lockName = LOCK_PREFIX_KEY + id;

lockService.forceReleaseLock(lockName);

//other process
}

In startProcessAPI method, we add lock and in endProcessAPI, we force release lock with same lockKey.

I hope, it was useful for you.

--

--

Serdar A.
Serdar A.

Written by Serdar A.

Senior Software Developer & Architect at Havelsan Github: https://github.com/serdaralkancode #Java & #Spring & #BigData & #React & #Microservice

No responses yet