源码见gitee
链接: https://gitee.com/qiu_yunzhao/distributed_lock.
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.haoqian</groupId>
<artifactId>redission_lock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redission_lock</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件
redisson:
# 单机
address: 127.0.0.1:6931
password: aaaaaa
database: 10
# # 哨兵集群
# master-name: mymaster
# password: aaaaaa
# sentinel-addresses:
# - 10.47.91.83:26379
# - 10.47.91.83:26380
# - 10.47.91.83:26381
redisson配置文件的装配类
package com.haoqian.redission_lock.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
private int timeout = 3000; // 连接超时时间
private String address; // 单机redis地址
private String password; // redis密码
private int database = 0; // 使用的redis库号
private int connectionPoolSize = 64; // 连接池大小
private int connectionMinimumIdleSize = 10; // 连接池中的最小空闲连接
private int slaveConnectionPoolSize = 250; // 从节点连接池大小
private int masterConnectionPoolSize = 250; // 主节点连接池大小
private String masterName; // 主节点名称
private String[] sentinelAddresses; // 集群地址
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String[] sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses;
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getDatabase() {
return this.database;
}
public void setDatabase(int database) {
this.database = database;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}
启动springboot是 RedissonClient 注入到spring容器,并装配工具类RedissonLockUtil
package com.haoqian.redission_lock.config;
import com.haoqian.redission_lock.distributedLock.DistributedLocker;
import com.haoqian.redission_lock.distributedLock.RedissonDistributedLocker;
import com.haoqian.redission_lock.Utils.RedissonLockUtil;
import io.micrometer.core.instrument.util.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(Config.class) // org.redisson.config.Config
@EnableConfigurationProperties(RedissonProperties.class) // 自定义redisson配置类
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redissonProperties;
/**
* 哨兵模式自动装配
*
* @return
*/
@Bean
@ConditionalOnProperty(name = "redisson.master-name")
RedissonClient redissonSentinelClient() {
Config config = new Config();
SentinelServersConfig serverConfig = config.useSentinelServers()
.addSentinelAddress(redissonProperties.getSentinelAddresses())
.setMasterName(redissonProperties.getMasterName())
.setTimeout(redissonProperties.getTimeout())
.setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
.setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize())
.setDatabase(redissonProperties.getDatabase());
if (StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 单机模式自动装配
*
* @return
*/
@Bean
@ConditionalOnProperty(name = "redisson.address")
RedissonClient redissonSingleClient() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress("redis://" + redissonProperties.getAddress())
.setTimeout(redissonProperties.getTimeout())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize())
.setDatabase(redissonProperties.getDatabase());
if (StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 装配locker类,并将实例注入到 RedissLockUtil 中
*
* @return
*/
@Bean
DistributedLocker distributedLocker(RedissonClient redissonClient) {
RedissonDistributedLocker locker = new RedissonDistributedLocker(redissonClient);
RedissonLockUtil.setLocker(locker);
return locker;
}
}
自定义封装Redisson分布式锁的lock接口
package com.haoqian.redission_lock.distributedLock;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁接口
*/
public interface DistributedLocker {
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
*
* @param lockKey
*/
void lock(String lockKey);
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
* 如果获取到锁后,执行结束后调用 unlock(String lockKey) 解锁或达到超时时间后会自动释放锁
* 默认时间单位是 TimeUnit.SECONDS
*
* @param lockKey
* @param leaseTime
*/
void lock(String lockKey, int leaseTime);
/**
* 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
* 如果获取到锁后,执行结束后调用 unlock(String lockKey) 解锁或达到超时时间后会自动释放锁
*
* @param lockKey // 锁唯一名称
* @param leaseTime // 持有锁的时间
* @param unit // 指定 leaseTime 的时间单位
*/
void lock(String lockKey, int leaseTime, TimeUnit unit);
/**
* 尝试获取锁,获取到立即返回true,未获取到立即返回false
*
* @param lockKey
* @return
*/
boolean tryLock(String lockKey);
/**
* 尝试获取锁,在等待时间内获取到锁则返回true,否则返回false,
* 如果获取到锁,执行完后程序调用 unlock(String lockKey) 释放锁或在给定的超时时间leaseTime后释放锁
*
* @param lockKey
* @param waitTime // 尝试获取锁的等待时间
* @param leaseTime // 持有锁的时间
* @param unit // 指定 leaseTime 的时间单位
* @return
*/
boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
/**
* 锁是否被任意一个线程锁持有
*
* @param lockKey
* @return
*/
boolean isLocked(String lockKey);
/**
* 释放锁
*
* @param lockKey
*/
void unlock(String lockKey);
}
自定义封装Redisson分布式锁的lock接口实现类
package com.haoqian.redission_lock.distributedLock;
import com.haoqian.redission_lock.distributedLock.DistributedLocker;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class RedissonDistributedLocker implements DistributedLocker {
private RedissonClient redissonClient;
public RedissonDistributedLocker() {
}
public RedissonDistributedLocker(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
@Override
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
}
@Override
public void lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
}
@Override
public void lock(String lockKey, int leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, unit);
}
@Override
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock();
}
@Override
public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock(waitTime, leaseTime, unit);
}
@Override
public boolean isLocked(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.isLocked();
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
}
redis分布式锁工具类
package com.haoqian.redission_lock.Utils;
import com.haoqian.redission_lock.distributedLock.DistributedLocker;
import java.util.concurrent.TimeUnit;
/**
* redisson 分布式锁帮助类
*/
public class RedissonLockUtil {
private static DistributedLocker redissonLocker;
public static void setLocker(DistributedLocker locker) {
redissonLocker = locker;
}
/**
* 获取锁
*
* @param lockKey
*/
public static void lock(String lockKey) {
redissonLocker.lock(lockKey);
}
/**
* 获取锁,超时释放
*
* @param lockKey
* @param leaseTime 持有锁的时间(单位:秒)
*/
public static void lock(String lockKey, int leaseTime) {
redissonLocker.lock(lockKey, leaseTime);
}
/**
* 获取锁,超时释放,指定时间单位
*
* @param lockKey
* @param leaseTime 持有锁的时间
* @param unit leaseTime的单位
*/
public static void lock(String lockKey, int leaseTime, TimeUnit unit) {
redissonLocker.lock(lockKey, leaseTime, unit);
}
/**
* 尝试获取锁,获取到立即返回true,获取失败立即返回false
*
* @param lockKey
* @return
*/
public static boolean tryLock(String lockKey) {
return redissonLocker.tryLock(lockKey);
}
/**
* 尝试获取锁,在给定的waitTime时间内尝试,获取到返回true,获取失败返回false,获取到后再给定的leaseTime时间超时释放
*
* @param lockKey
* @param waitTime
* @param leaseTime
* @param unit
* @return
* @throws InterruptedException
*/
public static boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
return redissonLocker.tryLock(lockKey, waitTime, leaseTime, unit);
}
/**
* 锁是否被任意一个线程锁持有
*
* @param lockKey
* @return
*/
public static boolean isLocked(String lockKey) {
return redissonLocker.isLocked(lockKey);
}
/**
* 解锁
*
* @param lockKey
*/
public static void unlock(String lockKey) {
redissonLocker.unlock(lockKey);
}
}
主启动类
package com.haoqian.redission_lock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RedissonLockApplication {
public static void main(String[] args) {
SpringApplication.run(RedissonLockApplication.class, args);
}
}
测试类
package com.haoqian.redission_lock.controller;
import com.haoqian.redission_lock.Utils.RedissonLockUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class TestController {
/**
* lock 会阻塞知道获取到锁
*/
@GetMapping("/testLock1")
public void testLock1() {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
RedissonLockUtil.lock("RedissonLock");
System.out.println(Thread.currentThread().getName() + "获取到锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
RedissonLockUtil.unlock("RedissonLock");
System.out.println(Thread.currentThread().getName() + "释放锁");
}
}
}).start();
}
}
/**
* tryLock
* 1. 获取到锁后超过leaseTime会自动释放锁
* 2. 超过waitTime未获取到锁会返回false放弃获取锁
*/
@GetMapping("/testLock2")
public void testLock2() {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
boolean result = RedissonLockUtil.tryLock("Lock:lock2", 3, 2, TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName() + "是否获取到锁" + result);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}).start();
}
}
/**
* 获取几次要释放几次,否则会阻塞
*/
@GetMapping("/testLock3")
public void testLock3() {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
RedissonLockUtil.lock("RedissonLock");
RedissonLockUtil.lock("RedissonLock");
System.out.println(Thread.currentThread().getName() + "获取到锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
RedissonLockUtil.unlock("RedissonLock");
// RedissonLockUtil.unlock("RedissonLock");
System.out.println(Thread.currentThread().getName() + "释放锁");
}
}
}).start();
}
}
}
http://localhost:8080/testLock1 接口测试结果:
http://localhost:8080/testLock2 接口测试结果:
http://localhost:8080/testLock3 接口测试结果:
如果获取锁的时候redis中已经存在相同的key说明有线程已经获取了锁,不能对资源进行操作;
如果获取锁的时候redis中不存在相同的key说明没有线程获取到锁,可以对资源进行操作;
如果对redisson的原理感兴趣,可以参考我写的另一篇文章
链接: https://blog.csdn.net/QiuHaoqian/article/details/114311189.