Mastering Distributed Locks with Spring Integration in Spring Boot
This guide explains how to use Spring Integration's LockRegistry for distributed locking, covering both database‑based and Redis‑based implementations, complete with Maven dependencies, configuration snippets, bean definitions, and comprehensive test cases to ensure reliable synchronization across multiple nodes.
Environment: SpringBoot 2.7.12
This article introduces the distributed lock functionality provided by Spring Integration.
1. Overview
Spring Integration is a framework for building event‑driven applications. Within it, the LockRegistry interface manages distributed locks, which synchronize access to shared resources across multiple nodes.
The LockRegistry and related sub‑interfaces (e.g., RenewableLockRegistry ) provide three main capabilities:
Acquire lock : obtain a lock via LockRegistry when a shared resource is needed.
Release lock : after using the resource, the lock is automatically released internally.
Renew : extend the lock’s hold time when necessary.
Common LockRegistry implementations include database‑based, ZooKeeper, and Redis‑based locks.
2. Database‑Based Distributed Lock
Dependencies
<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency></code>Configuration
<code>spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring_lock?serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&useSSL=false
username: root
password: xxxooo
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimumIdle: 10
maximumPoolSize: 200
---
spring:
integration:
jdbc:
initialize-schema: always
schema: classpath:schema-mysql.sql</code>Register core beans
<code>@Bean
public DefaultLockRepository defaultLockRepository(DataSource dataSource) {
DefaultLockRepository lockRepository = new DefaultLockRepository(dataSource);
// configure table prefix if needed, default: IN_
lockRepository.setPrefix("T_");
return lockRepository;
}
@Bean
public JdbcLockRegistry jdbcLockRegistry(DefaultLockRepository lockRepository) {
return new JdbcLockRegistry(lockRepository);
}</code>Test case
<code>@Test
public void testLock() throws Exception {
int len = 10;
CountDownLatch cdl = new CountDownLatch(len);
CountDownLatch waiter = new CountDownLatch(len);
Thread[] ts = new Thread[len];
for (int i = 0; i < len; i++) {
ts[i] = new Thread(() -> {
waiter.countDown();
System.out.println(Thread.currentThread().getName() + " - 准备获取锁");
try { waiter.await(); } catch (InterruptedException e1) { e1.printStackTrace(); }
// acquire lock
Lock lock = registry.obtain("drug_store_key_001");
lock.lock();
System.out.println(Thread.currentThread().getName() + " - 获取锁成功");
try {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
} finally {
// release lock
lock.unlock();
cdl.countDown();
System.out.println(Thread.currentThread().getName() + " - 锁释放成功");
}
}, "T - " + i);
}
for (int i = 0; i < len; i++) { ts[i].start(); }
cdl.await();
}</code>Database diagram
The JdbcLock implementation of java.util.concurrent.locks.Lock supports re‑entrancy. The retry interval after a lock acquisition failure defaults to 100 ms.
<code>JdbcLockRegistry jdbcLockRegistry = new JdbcLockRegistry(lockRepository);
// set retry interval when lock acquisition fails
jdbcLockRegistry.setIdleBetweenTries(Duration.ofMillis(200));</code>Lock renewal
<code>jdbcLockRegistry.renewLock("drug_store_key_001");</code>3. Redis‑Based Distributed Lock
Dependencies
<code><dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency></code>Configuration
<code>spring:
redis:
host: localhost
port: 6379
password: xxxooo
database: 8
lettuce:
pool:
maxActive: 8
maxIdle: 100
minIdle: 10
maxWait: -1</code>Test case (same as JDBC, only lock acquisition line changes)
<code>Lock lock = redisLockRegistry.obtain("001");</code>Set lock expiration (default 60 s)
<code>// third argument sets key expiration, here changed to 10 s
RedisLockRegistry redisLockRegistry = new RedisLockRegistry(connectionFactory, registryKey, 10000);
</code>Note: If the Redis key expires after 10 s, the lock will be released and further operations may fail because there is no Redisson watchdog mechanism.
<code>Exception in thread "T - 0" java.lang.IllegalStateException: Lock was released in the store due to expiration. The integrity of data protected by this lock may have been compromised.
at org.springframework.integration.redis.util.RedisLockRegistry$RedisLock.unlock(RedisLockRegistry.java:450)
at com.pack.SpringIntegrationDemoApplicationTests.lambda$1(SpringIntegrationDemoApplicationTests.java:83)
at java.lang.Thread.run(Thread.java:748)</code>Even after the key is deleted, other threads cannot immediately acquire the lock because a local ReentrantLock must be obtained first.
<code>private abstract class RedisLock implements Lock {
private final ReentrantLock localLock = new ReentrantLock();
public final void lock() {
this.localLock.lock();
while (true) {
try {
if (tryRedisLock(-1L)) {
return;
}
} catch (InterruptedException e) {
} catch (Exception e) {
this.localLock.unlock();
rethrowAsLockException(e);
}
}
}
}</code>Note: Both database‑based and Redis‑based locks require acquiring a local lock first.
Spring Cloud Task uses the database‑based lock from Spring Integration.
Conclusion: Spring Integration’s distributed lock provides a reliable synchronization method for distributed systems. By selecting the appropriate implementation (database or Redis) and configuring it correctly, developers can ensure coordinated access to shared resources, improving overall system reliability and performance.
Finished!!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.