石家庄网站建设培训班,网站开发大致多少钱,为什么要做官方网站,网页设计下载方式实战篇-09.分布式锁-基本原理和不同实现方式对比_哔哩哔哩_bilibili
1.分布式锁
因为jvm内部的sychonized锁无法在不同jvm之间共享锁监视器#xff0c;所以需要一个jvm外部的锁来共享。 2.redis setnx互斥锁
加锁解锁即可 2.1不释放锁可能死锁
redis 的setnx不会自动释放锁…实战篇-09.分布式锁-基本原理和不同实现方式对比_哔哩哔哩_bilibili
1.分布式锁
因为jvm内部的sychonized锁无法在不同jvm之间共享锁监视器所以需要一个jvm外部的锁来共享。 2.redis setnx互斥锁
加锁解锁即可 2.1不释放锁可能死锁
redis 的setnx不会自动释放锁要是加锁后服务宕机锁得不到释放可能死锁。
所以需要给锁加过期时间。 2.2保证加锁和过期时间的原子性 用set 参数的方式同时设置锁和过期时间保证不会因为过期时间没来及设置就宕机导致死锁 最终版本 到此为止基本完成了分布式锁但是还可以加以改进 2.3.其他线程失败后是否阻塞 一般用非阻塞式阻塞式浪费cpu而且实现麻烦。 阻塞式就是发现别人用锁就一直等待。 非阻塞式就是别人拿锁我就返回。 3.实现redis set nx分布式锁 3.1获取redis分布式锁
private String name; //业务名字private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX lock:; //规范名字private static final String ID_PREFIX UUID.randomUUID() -;private static final DefaultRedisScriptLong UNLOCK_SCRIPT;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean tryLock(long timeoutSec) {//获取线程标示String threadId ID_PREFIX Thread.currentThread().getId();//获取锁 set key value NX EX 过期时间Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); //防拆箱空指针} 3.2释放redis分布式锁 4.业务使用redis分布式锁
在订单创建业务那里把sychnoized锁改成自己实现的分布式锁获取解锁 5.服务阻塞导致分布式锁误删问题判断锁的线程标识 业务1阻塞时间太长导致锁过期自动删除 5.1解决方式判断线程标识符是否是自己的需要一个全局唯一线程标识符 每个jvm内部的线程号是一种递增的数字但是不同的jvm之间线程号可能冲突所以需要找一种方法 区分不仅jvm内部而且jvm之间的线程。 uuid是一种唯一识别码能保证不同的服务jvm的uuid一定不一样。 所以用 uuid jvm内部线程id的方式来唯一标识所有jvm中的线程 小科普通用唯一标识码UUID的介绍及使用 - 知乎 (zhihu.com)
5.1.1实现加锁和释放时加上了唯一线程标识判断 5.2另一种误删问题判断完之后阻塞丢锁后面又释放需要保证线程id和释放锁的原子性 实战篇-15.分布式锁-Lua脚本解决多条命令原子性问题_哔哩哔哩_bilibili
5.2.1保证原子性--lua脚本
调用redis提供的call函数传入redis命令参数 为了传参而把参数位留空后 5.2.2java调用lua脚本 提前读取好lua文件避免频繁读取等会调用。 为了维持 释放锁时 判断线程id和释放锁操作的原子性重写unlcok方法 6.最终该类代码
package com.hmdp.utils;import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;public class SimpleRedisLock implements ILock {private String name; //业务名字private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX lock:; //规范名字private static final String ID_PREFIX UUID.randomUUID() -;private static final DefaultRedisScriptLong UNLOCK_SCRIPT;static {UNLOCK_SCRIPT new DefaultRedisScript();UNLOCK_SCRIPT.setLocation(new ClassPathResource(unlock.lua));UNLOCK_SCRIPT.setResultType(Long.class);}public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean tryLock(long timeoutSec) {//获取线程标示String threadId ID_PREFIX Thread.currentThread().getId();//获取锁 set key value NX EX 过期时间Boolean success stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success); //防拆箱空指针}Overridepublic void unlock() {//调用lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX name),ID_PREFIX Thread.currentThread().getId());}/**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);}}* */
}