(一)基本原理和不同实现方式对比

sycn利用内部的锁监视器来控制锁,在jvm内部,只有一个线程能够获得锁监视器,因此可以实现线程互斥;当有多个jvm有多个锁监视器,就无法实现不同jvm下线程互斥的问题,因为同一时间可以有多个线程获取锁监视器。

分布式锁:满足分布系统下或集群模式下多进程可见并互斥的锁


(二)redis的分布式锁实现思路


(三)实现redis分布式锁版本1

public interface ILock {
// 尝试获取锁
boolean tryLock(long timeoutSec);
// 释放锁
void unlock();
}
public class SimpleRedisLock implements ILock{
private StringRedisTemplate stringRedisTemplate;
private String name;
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
private static final String KEY_PREFIX = "lock:";
@Override
public boolean tryLock(long timeoutSec) {
// 获取线程标识
long threadId = Thread.currentThread().getId();
// 获取锁
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutSec, TimeUnit.SECONDS);
// 避免空指针风险
return Boolean.TRUE.equals(success);
}
@Override
public void unlock() {
// 释放锁
stringRedisTemplate.delete(KEY_PREFIX + name);
}
}
// 创建锁对象
SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId);
// 获取锁
boolean isLock = lock.tryLock(1200);
// 判断获取锁是否成功
if(!isLock){
// 获取锁失败,返回错误信息并且重试
return Result.fail("不允许重复下单");
}
try {
// 获取代理对象(事务)
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
return proxy.createVoucherOrder(voucherId);
}finally {
// 释放锁
lock.unlock();
}
利用集群进行测试



同一个用户在两个不同jvm的线程下同时发起请求,只能获取到一把锁。此时分布式锁生效。

(四)redis分布式锁误删问题



(五)解决Redis分布式锁误删问题

public class SimpleRedisLock implements ILock{
private StringRedisTemplate stringRedisTemplate;
private 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) + "-";
@Override
public 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);
}
@Override
public 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);
}
}
}
(六)分布式锁的原子性问题

(七)Lua脚本解决多条命令原子性问题






(八)java调用lua脚本改造分布式锁

"unlock.lua"创建lua文件在resources下
if(redis.call('get', KEYS[1]) == ARGV[1]) then
return redis.call('del', KEYS[1])
end
return 0
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
UNLOCK_SCRIPT = new DefaultRedisScript<>();
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
UNLOCK_SCRIPT.setResultType(Long.class);
}
@Override
public void unlock() {
// 调用lua脚本
stringRedisTemplate.execute(
UNLOCK_SCRIPT,
Collections.singletonList(KEY_PREFIX + name),
ID_PREFIX + Thread.currentThread().getId());
}

(八)Redisson功能介绍


(九)Redisson快速入门


<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("你的密码");
return Redisson.create(config);
}
}
@Resource
private RedissonClient redissonClient;
// 创建锁对象
// SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId);
RLock lock = redissonClient.getLock("lock:order:" + userId);
// 获取锁
boolean isLock = lock.tryLock();
(八)Redisson的可重入原理




(九)Redisson的锁重试和看门狗机制
叽里呱啦的全是源码,这里上两张图吧


(十)Redisson的multiLock原理


配置三个redis节点,这里看视频就行,理解思路




4401



被折叠的 条评论
为什么被折叠?



