美文网首页
资深架构师手写教你redis分布式锁简单实现

资深架构师手写教你redis分布式锁简单实现

作者: 编辑小猿 | 来源:发表于2019-06-23 16:06 被阅读0次

redis分布式锁的基本功能包括, 同一刻只能有一个人占有锁, 当锁被其他人占用时, 获取者可以等待他人释放锁, 此外锁本身必须能超时自动释放.

直接上java代码, 如下:

package com.test;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import java.util.Arrays;

import java.util.concurrent.TimeUnit;

/**

* 简单的单实例redis分布式锁

* 没有实现的高级功能:锁的重入、锁的续约等

*

* @Author:tt

* @Description:

* @CreateTime:2019/6/12

*/

public class SingleRedisLock {

private JedisPool jedisPool;

/**

* 获取锁

*

* @param lockKey 锁的key

* @param lockVal 锁的val,可以利用来实现"避免误删别人锁"、"锁的重入"等功能

* @param lockMaxLifeTime 锁的最大生命时长,到期自动销毁,单位:毫秒

* @param tryWaitingTime 等待获取锁的超时时间,单位:毫秒

* @param waitingSleepTime 等待获取锁的阻塞周期,单位:毫秒,设置过短会造成cpu竞争,设置过长会造成浪费,需依赖于'具体业务平均的执行时长'

* @return

*/

public Boolean tryLock(String lockKey, String lockVal, int lockMaxLifeTime, int tryWaitingTime, int waitingSleepTime) {

//lua脚本,让逻辑简单清晰,同时保证原子性

//setNX:成功-1,失败-0

String lua = " if redis.call('set',KEYS[1],ARGV[1],'PX',ARGV[2],'NX') then return 1 else return 0 end ";

//获取锁的开始时间

Long tryBeginTime = System.currentTimeMillis();

//轮询

while (true) {

Long result = null;

Jedis jedis = null;

try {

jedis = jedisPool.getResource();

result = (Long) jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal, String.valueOf(lockMaxLifeTime)));

} catch (Exception e) {

e.printStackTrace();

} finally {

if (jedis != null) {

try {

jedis.close();

} catch (Exception e) {

}

}

}

//获取锁成功

if (Long.valueOf(1).equals(result)) {

return true;

}

//当前时间

Long now = System.currentTimeMillis();

//获取等待超时,就不用获取了

if (now - tryBeginTime >= tryWaitingTime) {

return false;

}

try {

//阻塞等一会儿再重新去获取

TimeUnit.MILLISECONDS.sleep(waitingSleepTime);

} catch (InterruptedException e) {

}

}

}

/**

* 释放锁

*

* @param lockKey

* @param lockVal

* @return

*/

public void releaseLock(String lockKey, String lockVal) {

//如果lockVal是自己的再删除,防止误删,场景来源:当前锁的持有者操作时间太长,锁已经自动释放并被别人占有了

String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) end ";

Jedis jedis = null;

try {

jedis = jedisPool.getResource();

jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal));

} catch (Exception e) {

e.printStackTrace();

} finally {

if (jedis != null) {

try {

jedis.close();

} catch (Exception e) {

}

}

}

}

//测试

public static void main(String[] args) {

//连接池

JedisPool jedisPool = new JedisPool(new GenericObjectPoolConfig(), "127.0.0.1", 6379, 2000, "test123");

SingleRedisLock simpleRedisLock = new SingleRedisLock();

simpleRedisLock.jedisPool = jedisPool;

//模拟10个并发

for (int i = 0; i < 10; i++) {

new Thread(() -> {

String lockKey = "TEST_LOCK_KEY";

String threadName = Thread.currentThread().getName();

//获取锁

Boolean locked = simpleRedisLock.tryLock(lockKey, threadName,

30000, 5000, 200);

//获取锁失败

if (!locked) {

System.err.println(">>> " + threadName + " 获取锁失败");

return;

}

//获取锁成功,模拟执行业务操作

System.out.println(">>> " + threadName + " 获取锁成功");

doShortBusiness();

//doLongBusiness();

//释放锁

simpleRedisLock.releaseLock(lockKey, threadName);

}).start();

}

try {

TimeUnit.MILLISECONDS.sleep(60000);

} catch (InterruptedException e) {

}

}

//短任务:100毫秒

static void doShortBusiness() {

try {

TimeUnit.MILLISECONDS.sleep(100);

} catch (InterruptedException e) {

}

}

//长任务:3秒

static void doLongBusiness() {

try {

TimeUnit.MILLISECONDS.sleep(3000);

} catch (InterruptedException e) {

}

}

}

锁的高级功能包含锁的重入、锁的续约等, 当然为了保证锁的高可用, redis还有主从、集群等部署方式, 对应的锁的实现也有区别, 略微复杂, 不过有现成的框架可供我们参考使用, 比较知名的如Redisson, 一个强大的redis客户端, 当然包括对“分布式锁”的完美实现, 其支持redis单实例、哨兵、集群等模式。

相关文章

网友评论

      本文标题:资深架构师手写教你redis分布式锁简单实现

      本文链接:https://www.haomeiwen.com/subject/ddtwqctx.html