单实例redis分布式锁的简单实现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单实例redis分布式锁的简单实现相关的知识,希望对你有一定的参考价值。
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分布式锁的简单实现的主要内容,如果未能解决你的问题,请参考以下文章