一淘模板给人人带来了对于于redis的相干知识,其中主要介绍了对于于散布式锁是甚么?Redis又是怎么实现散布式锁的?需要满足甚么条件?下面一起来看一下吧,希望对于需要的朋侪有帮助。
散布式锁:满足散布式系统或者集群形式下多进程可见而且互斥的锁。
散布式锁应当满足的条件:
可见性:多个线程都能看到相同的效果,细致:这其中央说的可见性并非并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思
互斥:互斥是散布式锁的最基础的条件,使患上程序串行实行
高可用:程序不易崩溃,常常刻刻都保障较高的可用性
高性能:因为加锁自身就让性能升高,所有对于散布式锁自身需要他就较高的加锁性能以及开释锁性能
平安性:平安也是程序中必不可少的一环
罕见的散布式锁有三种:
Mysql:mysql自身就带有锁机制,然而因为mysql性能自身失常,以是接收散布式锁的状况下,实在应用mysql作为散布式锁对于比少见
Redis:redis作为散布式锁是非常罕见的一种应用形式,现在企业级开辟中基础都应用redis或者zookeeper作为散布式锁,应用setnx这个方法,如果插入key胜利,则示意获取到了锁,如果有人插入胜利,其余人插入失利则示意无奈获取到锁,应用这套逻辑来实现散布式锁
Zookeeper:zookeeper也是企业级开辟中较好的一个实现散布式锁的计划
实现散布式锁时需要实现的两个基础方法:
获取锁:
互斥:确保只能有一个线程获取锁
非阻塞:试验一次,胜利返回true,失利返回false
开释锁:
手动开释
超时开释:获取锁时增添一个超时间阴
基于Redis实现散布式锁情理:
SET resource_name my_random_value NX PX 30000
resource_name:资本名称,可依据分比方的营业区分分比方的锁
my_random_value:随机值,每一个线程的随机值都分比方,用于开释锁时的校验
NX:key不存在时配置胜利,key存在则配置不胜利
PX:主动失效时日,浮现异样状况,锁能够过期失效
应用NX的原子性,多个线程并发时,只有一个线程能够配置胜利,配置胜利示意获取锁,能够实行后续的营业解决;如果浮现异样,过了锁的实用期,锁主动开释;
一、界说ILock接口
public interface ILock extends AutoCloseable {/** * 试验获取锁 * * @param timeoutSec 锁持有的超时间阴,过期后主动开释 * @return true代表获取锁胜利;false代表获取锁失利 */boolean tryLock(long timeoutSec); /** * 开释锁 * @return */void unLock();}
二、基于Redis实现散布式锁—RedisLock
public class SimpleRedisLock {private final StringRedisTemplate stringRedisTemplate;private final String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;} private static final String KEY_PREFIX = "lock:"; @Overridepublic boolean tryLock(long timeoutSec) {//获取线程标识String threadId = Thread.currentThread().getId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);} @Overridepublic void unLock() {//通过del删除了锁stringRedisTemplate.delete(KEY_PREFIX + name);} @Overridepublic void close() {unLock();}}
问题说明:
持有锁的线程1在锁的外部浮现了阻塞,这时锁超时主动开释,这时线程2试验获取锁,而后线程2在持有锁实前进程中,线程1反应过去,连续实行,走到了删除了锁逻辑,此时就会把本应当属于线程2的锁停止删除了,这便是锁误删的状况。
解决心划:
在存入锁时,放入自身线程的标识,在删除了锁时,坚定以后这把锁的标识是否自身存入的,如果是,则停止删除了,如果否,则一直止删除了。
public class SimpleRedisLock {private final StringRedisTemplate stringRedisTemplate;private final String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;} private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-"; @Overridepublic boolean tryLock(long timeoutSec) {//获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);} @Overridepublic void unLock() {// 获取线程标示String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁中的标示String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);// 坚定标示是否对于抗if(threadId.equals(id)) {// 开释锁stringRedisTemplate.delete(KEY_PREFIX + name);}} @Overridepublic void close() {unLock();}}
问题剖析:
上述开释锁的代码依然存在锁误删问题,当线程1获取锁中的线程标识,并依据标识坚定是自身的锁,这时锁到期主动开释,恰好线程2试验获取锁,并拿到了锁,此时线程1依然实行开释锁的操纵,就以致误删了线程2持有的锁。
原因在于,由java代码实现的开释锁流程不是原子操纵,存在线程平安问题。
解决心划:
Redis供应了Lua剧本色能,在一个剧本中编写多条Redis命令,能够确保多条命令实行时的原子性。
public class SimpleRedisLock implements ILock {private final StringRedisTemplate stringRedisTemplate;private final String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;} private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-"; @Overridepublic boolean tryLock(long timeoutSec) {//获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();//获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);} @Overridepublic void unLock() {String script = "if redis.call("get",KEYS[1]) == ARGV[1] then\n" +" return redis.call("del",KEYS[1])\n" +"else\n" +" return 0\n" +"end";//通过实行lua剧本实现锁删除了,能够校验随机值RedisScript redisScript = RedisScript.of(script, Boolean.class);stringRedisTemplate.execute(redisScript,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());} @Overridepublic void close() {unLock();}}
本文链接:https://addon.ciliseo.com/fen-bu-shi-suo-yuan-li-ji-redis-ru-he-shi-xian-fen-bu-shi-suo.html
网友评论
udnjulvzrm
回复如何实现为我们提供了一系列实际可操作的指南,让人们学会用智慧与行动把理想变成现实。
zigqumoz
回复问题不仅是思维的灯塔,更是探索未知世界的航标。
bjnehlp
回复到了是一部精彩的作品,无论是情感深度还是情节布局都展现了作者出色的文学功底。