近期有朋友问到分布式锁的相关问题,想起来前段时间在生成订单时减库存时牵扯到的多个客户端共享库存数所遇到的分布式锁问题,写了个简单的demo分享给各位同仁,如有不足之处还请指正,万分感谢。
源码地址:https://github.com/libenchu/redisDistributedLock
redis如何实现分布式锁?
1、首先pom依赖jedis包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2、 实现获取锁和释放锁的接口
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.UUID;
public class LockRedis {
// redis线程池
private JedisPool jedisPool;
private final String redisKey = "lock";
public LockRedis(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 获取锁
*
* @param beforeTimeOut 获取到锁之前到超时时间(秒)
* @param timeOut 获取到锁之后到超时时间(秒)
* @return value
*/
public String getLock(Long beforeTimeOut, Long timeOut) {
Jedis con = jedisPool.getResource();
String val = UUID.randomUUID().toString();
Long endTime = System.currentTimeMillis() + beforeTimeOut / 1000;
try {
//超时时间之前一直循环获取锁
while (System.currentTimeMillis() < endTime) {
if (con.setnx(redisKey, val) == 1) {
con.expire(redisKey, timeOut.intValue());
return val;
}
}
} catch (Throwable arg) {
arg.printStackTrace();
} finally {
if (con != null) {
con.close();
}
}
return null;
}
public void delLock(String val) {
Jedis con = jedisPool.getResource();
try {
if (con.get(redisKey).equals(val)) {
con.del(redisKey);
System.out.println(Thread.currentThread().getName() +"释放锁成功,锁的Id为" + val);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.close();
}
}
}
}
为避免多个线程在未获取到锁时占用大量系统资源去获取锁导致的性能问题,在getLock方法中第一个参数就是获取锁之前的超时时间,如果当前时间超过预期的超时时间,将主动放弃获取锁,而第二个参数设置的是获取到锁之后的超时时间,防止发生死锁。
而在释放锁的方法中注意一定要传入获锁的Id,也就是setnx参数中的value,避免发生A用户创建的锁让B用户给释放了的情况
3、设置连接redis配置封装获取锁释放锁业务
/**
* create by lishuai on 2019-06-23
*/
package com.me.ls;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class LockService {
private static JedisPool pool = null;
static {
JedisPoolConfig config = new JedisPoolConfig();
// 设置最大连接数
config.setMaxTotal(200);
// 设置最大空闲数
config.setMaxIdle(8);
// 设置最大等待时间
config.setMaxWaitMillis(1000 * 100);
// 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
config.setTestOnBorrow(true);
pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
}
private LockRedis lockRedis = new LockRedis(pool);
public void lockService() {
String val = lockRedis.getLock(5000l, 5000l);
if (val != null) {
System.out.println(Thread.currentThread().getName()+"获取锁成功,锁的Id为"+val);
lockRedis.delLock(val);
}else {
System.out.println(Thread.currentThread().getName()+"获取锁失败原因可能是超时,锁的Id为"+val);
}
}
}
4、简单测试
/**
* create by lishuai on 2019-06-22
*/
public class Test {
public static void main(String[] args) {
LockService lockService = new LockService();
for (int i=0;i<1000000;i++){
new ThreadRedis(lockService).run();
Thread thread=new Thread(new Runnable(){
public void run() {
new LockService().lockService();
};
});
}
}
}