当前位置: 首页 > news >正文

手表设计网站成都网站建设博客

手表设计网站,成都网站建设博客,wordpress改回旧版编辑器,html产品介绍网页设计代码作业面试题#xff1a; Redis除了拿来做缓存#xff0c;你还见过基于Redis的什么用法#xff1f; 1.数据共享#xff0c;分布式Session 2.分布式锁 3.全局ID 4.计算器、点赞 5.位统计 6.购物车 7.轻量级消息队列#xff1a;list、stream 8.抽奖 9.点赞、签到、打卡 10.差集交集…面试题 Redis除了拿来做缓存你还见过基于Redis的什么用法 1.数据共享分布式Session 2.分布式锁 3.全局ID 4.计算器、点赞 5.位统计 6.购物车 7.轻量级消息队列list、stream 8.抽奖 9.点赞、签到、打卡 10.差集交集并集用户关注、可能认识的人推荐模型 11.热点新间、热搜排行榜 Redis做分布式锁的时候有需要注意的问题你们公司自己实现的分布式锁是否用的setnx命令实现这个是最合适的吗你如何考虑分布式锁的可重入问题如果是Redis是单点部署的会带来什么问题那你准备怎么解决单点问题呢Redis集群模式下比如主从模式CAP方面有没有什么问题呢那你简单的介绍一下Redlock吧你简历上写redisson你谈谈Redis分布式锁如何续期看门狗知道吗 分布式锁需要具备的条件和刚需 独占性 OnlyOne任何时刻只能有且仅有一个线程持有 高可用 若redis集群环境下不能因为某一个节点挂了而出现获取锁和释放锁失败的情况高并发请求下依旧性能OK好使 防死锁 杜绝死锁必须有超时控制机制或者撤销操作有个兜底终止跳出方案 不乱抢 防止张冠李戴不能私下unlock别人的锁只能自己加锁自己释放自己约的锁含着泪也要自己解 重入性 同一个节点的同一个线程如果获得锁之后它也可以再次获取这个锁 分布式锁 官网set 命令 使用set进行占锁删掉key代表锁释放。 setnx key value 注setnxexpire不安全两条命令非原子性的。 set key value [EX seconds] [PX milliseconds] [NX|XX] 重点JUC中AQS锁的规范落地参考可重入锁考虑Lua脚本Redist命令一步步实现分布式锁 案例 V1.0 版本基础案例 使用场景多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击。 1、pom.xml文件 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.atguigu.redislock/groupIdartifactIdredis_distributed_lock2/artifactIdversion1.0-SNAPSHOT/versionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.6.12/versionrelativePath/ !-- lookup parent from repository --/parentpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.targetlombok.version1.16.18/lombok.version/propertiesdependencies!--SpringBoot通用依赖模块--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--SpringBoot与Redis整合依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId/dependency!--swagger2--dependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger2/artifactIdversion2.9.2/version/dependencydependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger-ui/artifactIdversion2.9.2/version/dependency!--通用基础配置boottest/lombok/hutool--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion${lombok.version}/versionoptionaltrue/optional/dependencydependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.8/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project 2、ymal文件 server.port7777spring.application.nameredis_distributed_lock # swagger2 # http://localhost:7777/swagger-ui.html swagger2.enabledtrue spring.mvc.pathmatch.matching-strategyant_path_matcher# redis单机 spring.redis.database0 spring.redis.host192.168.111.185 spring.redis.port6379 spring.redis.password111111 spring.redis.lettuce.pool.max-active8 spring.redis.lettuce.pool.max-wait-1ms spring.redis.lettuce.pool.max-idle8 spring.redis.lettuce.pool.min-idle0 3、主启动类 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class RedisDistributedLockApp {public static void main(String[] args) {SpringApplication.run(RedisDistributedLockApp.class,args);} } 4、配置类 import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import springfox.documentation.swagger2.annotations.EnableSwagger2;import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors;Configuration EnableSwagger2 public class Swagger2Config {Value(${swagger2.enabled})private Boolean enabled;Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(enabled).select().apis(RequestHandlerSelectors.basePackage(com.atguigu.redislock)) //你自己的package.paths(PathSelectors.any()).build();}private ApiInfo apiInfo() {return new ApiInfoBuilder().title(springboot利用swagger2构建api接口文档 \t DateTimeFormatter.ofPattern(yyyy-MM-dd).format(LocalDateTime.now())).description(springbootredis整合).version(1.0).termsOfServiceUrl(https://www.baidu.com/).build();}}import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;Configuration public class RedisConfig {Beanpublic RedisTemplateString, Object redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {RedisTemplateString,Object redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(lettuceConnectionFactory);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value的序列化方式jsonredisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;} } 5、业务类 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;public String sale() {String retMessage ;try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}} finally {// 释放操作}return retMessage\t服务端口号port;} } import cn.hutool.core.util.IdUtil; import com.atguigu.redislock.service.InventoryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.atomic.AtomicInteger;RestController Api(tags redis分布式锁测试) public class InventoryController {Autowiredprivate InventoryService inventoryService;ApiOperation(扣减库存一次卖一个)GetMapping(value /inventory/sale)public String sale() {return inventoryService.sale();} } 丝袜哥测试地址http://localhost:7777/swagger-ui.html#/ V2.0 版本 使用 synchronized 或者 lock 进行加锁操作 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;lock.lock();try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {lock.unlock();}return retMessage\t服务端口号port;} } 若在添加一个相同服务另一个服务为8888端口使用 nginx 轮训访问 7777 和 8888 服务此时就会出现超卖现象。lock 只针对当前服务有用锁不了其他服务。 Nginx配置负载均衡 1、常用命令 检查nginx.conf文件合法性     nginx -t -c ./conf/nginx.conf 检查nginx服务是否启动     tasklist /fi imagename eq nginx.exe 启动nginx     start nginx 或 ./nginx 重新加载     nginx -s reload 停止服务     nginx -s stop 2、配置文件 服务测试 启动 7777 和 8888 服务通过Nginx访问你的Linux服务器地址lP反向代理负裁均衡可以点击看到效果一边一个默认轮询http://192.168.0.185/inventory/sale 使用 jmeter 进行压测 http请求 测试结果 异常76号商品被卖出2次出现超卖故章现象。 结论 在单机环境下可以使用synchronized或Lock来实现。 但是在分布式系统中因为竞争的线程可能不在同一个节点上同一个jvm中所以需要一个让所有进程都能访问到的锁来实现(比如redis或者zookeeper来构建)。 不同进程jvm层面的锁就不管用了那么可以利用第三方的一个组件来获取锁未获取到锁则阻塞当前想要运行的线程。 V3.1 版本 使用递归进行重试 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;String key zzyyRedisLock;String uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();Boolean flag stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue);if(!flag){//暂停20毫秒后递归调用try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }sale();}else{try{//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {stringRedisTemplate.delete(key);}}return retMessage\t服务端口号port;} } 结果 1、手工测试OK使用Jmeter压测5000也OK 2、递归是一种思想没错但是容号导致StackOverflowError不太推荐需进一步完善 V3.2 版本 使用多线程判断想想JUC里面的虚假唤醒用while替代if用自旋替代送归重试。 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;String key zzyyRedisLock;String uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();while(!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue)){//暂停20毫秒类似CAS自旋try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }}try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {stringRedisTemplate.delete(key);}return retMessage\t服务端口号port;} } 问题部署了微服务的Java程序机器挂了代码层面根本没有走到finallyi这块没办法保证解锁(无过期时间该key一直存在)这个kev没有被删除需要加入一个过期时间限定key。 V4.1 版本 ...... while(!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue)) {//暂停20毫秒进行递归重试.....try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } ...... stringRedisTemplate.expire(key,30L,TimeUnit.SECONDS); ...... 结论虽然加了过期时间但是设置key过期时间分开了不具备原子性。 V4.2 版本 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;String key zzyyRedisLock;String uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();while(!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue,30L,TimeUnit.SECONDS)){//暂停毫秒try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }}try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {stringRedisTemplate.delete(key);}return retMessage\t服务端口号port;} } 使用Jmeter压测OK 结论加锁和过期时间设置必须同一行保证原子性。 V5.0 版本 4.2版本出现的问题实际业务处理时间如果超过了默认设晋key的过期时间怎么办张冠李戴别除了别人的锁只能自己除自己的不许动别人的 改进 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;String key zzyyRedisLock;String uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();while(!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue,30L,TimeUnit.SECONDS)) {//暂停毫秒try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }}try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber\tuuidValue;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {// v5.0判断加锁与解锁是不是同一个客户端同一个才行自己只能删除自己的锁不误删他人的if(stringRedisTemplate.opsForValue().get(key).equalsIgnoreCase(uuidValue)){stringRedisTemplate.delete(key);}}return retMessage\t服务端口号port;} } V6.0 版本小厂够用 上面5.0版本出现的问题finally块的判断del删除除操作不是原子性的。 解决方案启用lua脚本编写redis分布式锁判断删除判断代码。 原理Redis调用Lua本通过eval命令保证代码执行的原子性直接用return返回脚本执行后的结果值。 Lua 脚本 官网https://redis.io/docs/manual/patterns/distributed-locks/ 脚本格式 eval luascript numkeys [key [key ]][arg [arg ...]] 案例一输出文字 案例二组合命令 案例三执行命令 案例四条件判断 使用格式如下 eval if redis.call(get,KEYS[1])ARGV[1] then return redis.call(del,KEYS[1]) else return 0 end 1 zzyyRedisLock 1111-2222-3333 解决方式 import cn.hutool.core.util.IdUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service;import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;private Lock lock new ReentrantLock();public String sale() {String retMessage ;String key zzyyRedisLock;String uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();while(!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue,30L,TimeUnit.SECONDS)) {//暂停毫秒try { TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }}try {//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber\tuuidValue;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}}finally {//V6.0 将判断删除自己的合并为lua脚本保证原子性String luaScript if (redis.call(get,KEYS[1]) ARGV[1]) then return redis.call(del,KEYS[1]) else return 0 end;stringRedisTemplate.execute(new DefaultRedisScript(luaScript, Boolean.class), Arrays.asList(key), uuidValue);}return retMessage\t服务端口号port;} } BUG 说明 V7.0 版本可重入锁 6.0版本while判断并自旋重试获取锁setx含自然过期时间ua脚本官网除锐命令。那么问题来了如何兼顾锁的可重入性问题 一个靠谱分布式锁需要具备的条件和刚需独占性、高可用、防死锁、不乱抢、重入性 可重入锁 说明可重入锁又名递归锁是指在同一个线程在外层方法获取锁的时候再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象)不会因为之前已经获取过还没释放而阻塞。如果是1个有 synchronized 修饰的递归调用方法程序第2次进入被自己阻塞了岂不是天大的笑话出现了作茧自缚。所以Java中ReentrantLock和synchronized都是可重入锁可重入锁的一个优点是可一定程度避免死锁。 “可重入锁”这四个字分开来解释 可可以。 重再次。 入进入。 锁同步锁。 进入什么进入同步域(即同步代码快/方法或显式锁锁定的代码) 一句话一个线程中的多个流程可以获取同一把锁持有这把同步锁句以再次进入。自己可以获取自己的内部锁。 可重入锁种类 第一种隐式锁(即synchronized关键字使用的锁)默认是可重入锁。 指的是可重复可递归调用的锁在外层使用锁之后在内层仍然可以使用并且不发生死锁这样的锁就叫做可重入锁。 简单的来说就是在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时是永远可以得到锁的。 与可重入锁相反不可重入锁不可递归调用递归调用就发生死锁。 同步块 public class ReEntryLockDemo {public static void main(String[] args) {final Object objectLockA new Object();new Thread(() - {synchronized (objectLockA) {System.out.println(-----外层调用);synchronized (objectLockA) {System.out.println(-----中层调用);synchronized (objectLockA) {System.out.println(-----内层调用);}}}},a).start();} } 同步方法 /*** 在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时是永远可以得到锁的*/ public class ReEntryLockDemo {public synchronized void m1() {System.out.println(-----m1);m2();}public synchronized void m2() {System.out.println(-----m2);m3();}public synchronized void m3() {System.out.println(-----m3);}public static void main(String[] args) {ReEntryLockDemo reEntryLockDemo new ReEntryLockDemo();reEntryLockDemo.m1();} } 实现原理 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。当执行monitorenter时如果目标锁对象的计数器为零那么说明它没有被其他线程所持有Java虚拟机会将该锁对象的持有线程设置为当前线程并且将其计数器加1。在目标锁对象的计数器不为零的情况下如果锁对象的持有线程是当前线程那么 Java 虚拟机可以将其计数器加1否则需要等待直至持有线程释放该锁。当执行monitorexit时Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。 第二种显式锁(即Lock)也有ReentrantLock这样的可重入锁。 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;/*** 在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时是永远可以得到锁的*/ public class ReEntryLockDemo {static Lock lock new ReentrantLock();public static void main(String[] args) {new Thread(() - {lock.lock();try {System.out.println(----外层调用lock);lock.lock();try {System.out.println(----内层调用lock);}finally {// 这里故意注释实现加锁次数和释放次数不一样// 由于加锁次数和释放次数不一样第二个线程始终无法获取到锁导致一直在等待。lock.unlock(); // 正常情况加锁几次就要解锁几次}}finally {lock.unlock();}},a).start();new Thread(() - {lock.lock();try {System.out.println(b thread----外层调用lock);}finally {lock.unlock();}},b).start();} } 备注一般而言你lock了几次就要unlock几次。 Redis 可重入锁数据类型 使用key,key,value结构MapString,MapObject,Object hset  zzyyRedisLock 29f0ee01ac77414fb8b0861271902a94:1 案例 hset key field value hset redis锁名字(zzyyRedisLock)  某个请求线程的UUIDThreadID  加锁的次数 总结setnx,只能解决有无的问题够用但是不完美hset不但解决有无还解决可重入问题。 Lua脚本 上面版本扣减库存的核心代码保证原子性 实现要求lua脚本实现过程分析 一、加锁lua脚本其实就是 lock 步骤 1、先断redis分布式锁这个key是否存在 EXISTS key  2、返回零说明不存在hset新建当前线程属于自己的锁BY UUID:ThreadID 3、返回壹说明已经有锁需进一步判断是不是当前线程自己的HEXISTS key uuid:ThreadID 命令HEXISTS zzyyRedisLock 0c90d37cb6ec42268861b3d739f8b3a8:1 3.1、返回零说明不是自己的 3.2、返回壹说明是自己的锁自增1次表示重入 命令HINCRBY key field increment 案例HINCRBY zzyyRedisLock 0c90d37cb6ec42268861b3d739f8b3a8:1 1 根据以上步骤得出lua脚本 if redis.call(exists,key) 0 then   redis.call(hset,key,uuid:threadid,1)   redis.call(expire,key,30)   return 1 elseif redis.call(hexists,key,uuid:threadid) 1 then   redis.call(hincrby,key,uuid:threadid,1)   redis.call(expire,key,30)   return 1 else   return 0 end 以上相同部分是否可以替换处理 答案hincrby命令可否替代hset命令。自增命令没有key会创建key 优化脚本 if redis.call(exists,key) 0 or redis.call(hexists,key,uuid:threadid) 1 then   redis.call(hincrby,key,uuid:threadid,1)   redis.call(expire,key,30)   return 1 else   return 0 end 替换参数 if redis.call(exists,KEYS[1]) 0 or redis.call(hexists,KEYS[1],ARGV[1]) 1 then     redis.call(hincrby,KEYS[1],ARGV[1],1)     redis.call(expire,KEYS[1],ARGV[2])     return 1 else      return 0 end 参数说明 测试加锁lua脚本 EVAL if redis.call(exists,KEYS[1]) 0 or redis.call(hexists,KEYS[1],ARGV[1]) 1 then redis.call(hincrby,KEYS[1],ARGV[1],1) redis.call(expire,KEYS[1],ARGV[2]) return 1 else return 0 end 1 zzyyRedisLock 0c90d37cb6ec42268861b3d739f8b3a8:1 30 HGET zzyyRedisLock 0c90d37cb6ec42268861b3d739f8b3a8:1 二、解锁Lua脚本其实就是 unlock 步骤 1、先判断redis分布式锁这个key是否存在EXISTS key 2、返回零说明根本没有锁程序块返回nil 3、不是零说明有锁且是自己的锁直接调用 HINCRBY -1 表示每次减个一解领一次直到它变为零表示可以除该锁keydel锁key 根据以上步骤得出解锁lua脚本 if redis.call(HEXISTS,lock,uuid:threadID) 0 then     return nil elseif redis.call(HINCRBY,lock,uuid:threadID,-1) 0 then     return redis.call(del,lock) else      return 0 end 替换参数 if redis.call(HEXISTS,KEYS[1],ARGV[1]) 0 then     return nil elseif redis.call(HINCRBY,KEYS[1],ARGV[1],-1) 0 then     return redis.call(del,KEYS[1]) else     return 0 end 调试命令  eval if redis.call(HEXISTS,KEYS[1],ARGV[1]) 0 then return nil elseif redis.call(HINCRBY,KEYS[1],ARGV[1],-1) 0 then return redis.call(del,KEYS[1]) else return 0 end 1 zzyyRedisLock 2f586ae740a94736894ab9d51880ed9d:1 测试解锁lua脚本 整合Java程序 原程序初始无锁版 import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service;Service Slf4j public class InventoryService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Value(${server.port})private String port;public String sale() {String retMessage ;//1 查询库存信息String result stringRedisTemplate.opsForValue().get(inventory001);//2 判断库存是否足够Integer inventoryNumber result null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber 0) {stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));retMessage 成功卖出一个商品库存剩余: inventoryNumber\t;System.out.println(retMessage);}else{retMessage 商品卖完了o(╥﹏╥)o;}return retMessage\t服务端口号port;} } 使用工厂设计模式新建RedisDistributedLock类并实现UUC里面的Lock接口满足JUC里面AQS对Lock锁的接口规范定义来进行实现落地代码。 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.locks.Lock;Component public class DistributedLockFactory {Autowiredprivate StringRedisTemplate stringRedisTemplate;private String lockName;public Lock getDistributedLock(String lockType) {if(lockType null) return null;if(lockType.equalsIgnoreCase(REDIS)){lockName zzyyRedisLock;return new RedisDistributedLock(stringRedisTemplate,lockName);} else if(lockType.equalsIgnoreCase(ZOOKEEPER)){//TODO zookeeper版本的分布式锁实现return new ZookeeperDistributedLock();} else if(lockType.equalsIgnoreCase(MYSQL)){//TODO mysql版本的分布式锁实现return null;}return null;} } import cn.hutool.core.util.IdUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.support.collections.DefaultRedisList; import org.springframework.stereotype.Component;import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock;//Component 引入DistributedLockFactory工厂模式从工厂获得而不再从spring拿到 public class RedisDistributedLock implements Lock {private StringRedisTemplate stringRedisTemplate;private String lockName;//KEYS[1]private String uuidValue;//ARGV[1]private long expireTime;//ARGV[2]public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName) {this.stringRedisTemplate stringRedisTemplate;this.lockName lockName;this.uuidValue IdUtil.simpleUUID():Thread.currentThread().getId();//UUID:ThreadIDthis.expireTime 30L;}Overridepublic void lock(){tryLock();}Overridepublic boolean tryLock(){try {tryLock(-1L,TimeUnit.SECONDS);} catch (InterruptedException e) {e.printStackTrace();}return false;}/*** 干活的实现加锁功能实现这一个干活的就OK全盘通用* param time* param unit* return* throws InterruptedException*/Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException{if(time ! -1L){this.expireTime unit.toSeconds(time);}String script if redis.call(exists,KEYS[1]) 0 or redis.call(hexists,KEYS[1],ARGV[1]) 1 then redis.call(hincrby,KEYS[1],ARGV[1],1) redis.call(expire,KEYS[1],ARGV[2]) return 1 else return 0 end;System.out.println(script: script);System.out.println(lockName: lockName);System.out.println(uuidValue: uuidValue);System.out.println(expireTime: expireTime);while (!stringRedisTemplate.execute(new DefaultRedisScript(script,Boolean.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime))) {TimeUnit.MILLISECONDS.sleep(50);}return true;}/***干活的实现解锁功能*/Overridepublic void unlock() {String script if redis.call(HEXISTS,KEYS[1],ARGV[1]) 0 then return nil elseif redis.call(HINCRBY,KEYS[1],ARGV[1],-1) 0 then return redis.call(del,KEYS[1]) else return 0 end;// nil false 1 true 0 falseSystem.out.println(lockName: lockName);System.out.println(uuidValue: uuidValue);System.out.println(expireTime: expireTime);Long flag stringRedisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime));if(flag null) {throw new RuntimeException(This lock doesnt EXIST);}}//下面的redis分布式锁暂时用不到//下面的redis分布式锁暂时用不到//下面的redis分布式锁暂时用不到Overridepublic void lockInterruptibly() throws InterruptedException {}Overridepublic Condition newCondition() {return null;} } InventoryService使用工厂模式版   import ch.qos.logback.core.joran.conditional.ThenAction;import cn.hutool.core.util.IdUtil;import cn.hutool.core.util.StrUtil;import com.atguigu.redislock.mylock.DistributedLockFactory;import com.atguigu.redislock.mylock.RedisDistributedLock;import lombok.extern.slf4j.Slf4j;import org.omg.IOP.TAG_RMI_CUSTOM_MAX_STREAM_FORMAT;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;Service Slf4jpublic class InventoryService {     Autowired     private StringRedisTemplate stringRedisTemplate;     Value(${server.port})     private String port;    Autowired     private DistributedLockFactory distributedLockFactory;          public String sale()  {         String retMessage  ;        Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             //1 查询库存信息             String result  stringRedisTemplate.opsForValue().get(inventory001);             //2 判断库存是否足够             Integer inventoryNumber result  null ? 0 : Integer.parseInt(result);             //3 扣减库存             if(inventoryNumber  0) {                 inventoryNumber inventoryNumber - 1;                 stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(inventoryNumber));                 retMessage  成功卖出一个商品库存剩余: inventoryNumber\t服务端口: port;                 System.out.println(retMessage);                 return retMessage;             }             retMessage  商品卖完了o(╥﹏╥)o\t服务端口: port;         }catch (Exception e){             e.printStackTrace();         }finally {             redisLock.unlock();         }         return retMessage;     } } 可重入性加锁 测试代码改造InventoryService 类 import com.atguigu.redislock.mylock.DistributedLockFactory;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.concurrent.locks.Lock;Service Slf4jpublic class InventoryService {     Autowired     private StringRedisTemplate stringRedisTemplate;     Value(${server.port})     private String port;     Autowired     private DistributedLockFactory distributedLockFactory;     public String sale() {         String retMessage  ;         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             //1 查询库存信息             String result  stringRedisTemplate.opsForValue().get(inventory001);             //2 判断库存是否足够             Integer inventoryNumber result  null ? 0 : Integer.parseInt(result);             //3 扣减库存             if(inventoryNumber  0) {                 stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));                 retMessage  成功卖出一个商品库存剩余: inventoryNumber\t;                 System.out.println(retMessage);                testReEnter();             }else{                 retMessage  商品卖完了o(╥﹏╥)o;             }         }catch (Exception e){             e.printStackTrace();         }finally {             redisLock.unlock();         }         return retMessage\t服务端口号port;     }    private void testReEnter() {         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             System.out.println(################测试可重入锁#######);         }finally {             redisLock.unlock();         }     } }   测试结果ThreadID一致了但是UUID不OK。 改造代码 import cn.hutool.core.util.IdUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;import java.util.concurrent.locks.Lock;Componentpublic class DistributedLockFactory {     Autowired     private StringRedisTemplate stringRedisTemplate;     private String lockName;     private String uuidValue;     public DistributedLockFactory() {         this.uuidValue  IdUtil.simpleUUID();//UUID     }     public Lock getDistributedLock(String lockType) {         if(lockType  null) return null;         if(lockType.equalsIgnoreCase(REDIS)){            lockName  zzyyRedisLock;             return new RedisDistributedLock(stringRedisTemplate,lockName,uuidValue);         } else if(lockType.equalsIgnoreCase(ZOOKEEPER)){             //TODO zookeeper版本的分布式锁实现             return new ZookeeperDistributedLock();         } else if(lockType.equalsIgnoreCase(MYSQL)){             //TODO mysql版本的分布式锁实现             return null;         }         return null;     } }   import cn.hutool.core.util.IdUtil;import lombok.SneakyThrows;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import java.util.Arrays;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;public class RedisDistributedLock implements Lock {     private StringRedisTemplate stringRedisTemplate;     private String lockName;     private String uuidValue;     private long   expireTime;    public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName,String uuidValue) {         this.stringRedisTemplate  stringRedisTemplate;         this.lockName  lockName;         this.uuidValue  uuidValue:Thread.currentThread().getId();         this.expireTime  30L;     }     Override     public void lock() {         this.tryLock();     }     Override     public boolean tryLock() {         try {             return this.tryLock(-1L,TimeUnit.SECONDS);         } catch (InterruptedException e) {             e.printStackTrace();         }         return false;     }     Override     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {         if(time ! -1L) {             expireTime  unit.toSeconds(time);         }         String script                 if redis.call(exists,KEYS[1]) 0 or redis.call(hexists,KEYS[1],ARGV[1]) 1 then                       redis.call(hincrby,KEYS[1],ARGV[1],1)                       redis.call(expire,KEYS[1],ARGV[2])                       return 1                   else                       return 0                   end;         System.out.println(lockName: lockName\tuuidValue: uuidValue);         while (!stringRedisTemplate.execute(new DefaultRedisScript(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime))) {             try { TimeUnit.MILLISECONDS.sleep(60); } catch (InterruptedException e) { e.printStackTrace(); }         }         return true;     }     Override     public void unlock() {         String script                 if redis.call(HEXISTS,KEYS[1],ARGV[1]) 0 then                       return nil                   elseif redis.call(HINCRBY,KEYS[1],ARGV[1],-1) 0 then                       return redis.call(del,KEYS[1])                   else                           return 0                   end;         System.out.println(lockName: lockName\tuuidValue: uuidValue);         Long flag  stringRedisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime));         if(flag  null) {             throw new RuntimeException(没有这个锁HEXISTS查询无);         }     }     //     Override     public void lockInterruptibly() throws InterruptedException {     }     Override     public Condition newCondition() {         return null;     } } InventoryService类新增可重入测试方法 import cn.hutool.core.util.IdUtil;import com.atguigu.redislock.mylock.DistributedLockFactory;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;Service Slf4jpublic class InventoryService {     Autowired     private StringRedisTemplate stringRedisTemplate;     Value(${server.port})     private String port;     Autowired     private DistributedLockFactory distributedLockFactory;     public String sale() {         String retMessage  ;         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             //1 查询库存信息             String result  stringRedisTemplate.opsForValue().get(inventory001);             //2 判断库存是否足够             Integer inventoryNumber result  null ? 0 : Integer.parseInt(result);             //3 扣减库存             if(inventoryNumber  0) {                 stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));                 retMessage  成功卖出一个商品库存剩余: inventoryNumber;                 System.out.println(retMessage);                 this.testReEnter();             }else{                 retMessage  商品卖完了o(╥﹏╥)o;             }         }catch (Exception e){             e.printStackTrace();         }finally {             redisLock.unlock();         }         return retMessage\t服务端口号port;     }    private void testReEnter() {         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             System.out.println(################测试可重入锁####################################);         }finally {             redisLock.unlock();         }     } } 结果单机并发可重入性测试通过 V8.0版本自动续期 思考确保redisLock过期时间大于业务执行时间的问题Redis分布式如何续续期 什么是CAP C一致性所有的节点上的数据时刻保持同步A可用性每个请求都能接受到一个响应无论响应成功或失败P分区容错系统应该能持续提供服务即使系统内部有消息丢失分区 CA without P如果不要求P不允许分区则C强一致性和A可用性是可以保证的。但其实分区不是你想不想的问题而是始终会存在因此CA的系统更多的是允许分区后各子系统依然保持CA。CP without A如果不要求A可用相当于每个请求都需要在Server之间强一致而P分区会导致同步时间无限延长如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。AP wihtout C要高可用并允许分区则需放弃一致性。一旦分区发生节点之间可能会失去联系为了高可用每个节点只能用本地数据提供服务而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。   Redis集群是AP         redis异步复制造成的锁丢失比如主节点没来的及把刚刚set进来这条数据给从节点master就挂了从机上位但从机上无该数据。Zookeeper集群是CP 故障问题 Eureka集群是AP Nacos集群是AP lua代码加钟 脚本测试 hset zzyyRedisLock 111122223333:11 3 EXPIRE zzyyRedisLock 30 ttl zzyyRedisLock 。。。。。 eval if redis.call(HEXISTS,KEYS[1],ARGV[1]) 1 then return redis.call(expire,KEYS[1],ARGV[2]) else return 0 end 1 zzyyRedisLock 111122223333:11 30 ttl zzyyRedisLock lua脚本 //自动续期 if redis.call(HEXISTS,KEYS[1],ARGV[1]) 1 then   return redis.call(expire,KEYS[1],ARGV[2]) else   return 0 end 自动续期 import cn.hutool.core.util.IdUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.data.redis.support.collections.DefaultRedisList;import org.springframework.stereotype.Component;import java.util.Arrays;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;public class RedisDistributedLock implements Lock {     private StringRedisTemplate stringRedisTemplate;     private String lockName;//KEYS[1]     private String uuidValue;//ARGV[1]     private long   expireTime;//ARGV[2]     public RedisDistributedLock(StringRedisTemplate stringRedisTemplate,String lockName,String uuidValue) {         this.stringRedisTemplate  stringRedisTemplate;         this.lockName  lockName;         this.uuidValue  uuidValue:Thread.currentThread().getId();         this.expireTime  30L;     }     Override     public void lock() {         tryLock();     }     Override     public boolean tryLock() {         try {tryLock(-1L,TimeUnit.SECONDS);} catch (InterruptedException e) {e.printStackTrace();}         return false;     }     /**      * 干活的实现加锁功能实现这一个干活的就OK全盘通用      * param time      * param unit      * return      * throws InterruptedException      */     Override     public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {         if(time ! -1L) {             this.expireTime  unit.toSeconds(time);         }         String script                 if redis.call(exists,KEYS[1]) 0 or redis.call(hexists,KEYS[1],ARGV[1]) 1 then                           redis.call(hincrby,KEYS[1],ARGV[1],1)                           redis.call(expire,KEYS[1],ARGV[2])                           return 1                           else                           return 0                           end;         System.out.println(script: script);         System.out.println(lockName: lockName);         System.out.println(uuidValue: uuidValue);         System.out.println(expireTime: expireTime);         while (!stringRedisTemplate.execute(new DefaultRedisScript(script,Boolean.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime))) {             TimeUnit.MILLISECONDS.sleep(50);         }         this.renewExpire();         return true;     }     /**      *干活的实现解锁功能      */     Override     public void unlock() {         String script                 if redis.call(HEXISTS,KEYS[1],ARGV[1]) 0 then                              return nil                           elseif redis.call(HINCRBY,KEYS[1],ARGV[1],-1) 0 then                              return redis.call(del,KEYS[1])                           else                              return 0                           end;         // nil false 1 true 0 false         System.out.println(lockName: lockName);         System.out.println(uuidValue: uuidValue);         System.out.println(expireTime: expireTime);         Long flag  stringRedisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime));         if(flag  null) {             throw new RuntimeException(This lock doesnt EXIST);         }     }     private void renewExpire() {         String script                 if redis.call(HEXISTS,KEYS[1],ARGV[1]) 1 then                           return redis.call(expire,KEYS[1],ARGV[2])                           else                           return 0                           end;         new Timer().schedule(new TimerTask() {             Override             public void run() {                 if (stringRedisTemplate.execute(new DefaultRedisScript(script, Boolean.class), Arrays.asList(lockName),uuidValue,String.valueOf(expireTime))) {                     renewExpire();                 }             }         },(this.expireTime * 1000)/3);     }     //下面的redis分布式锁暂时用不到     //下面的redis分布式锁暂时用不到     //下面的redis分布式锁暂时用不到     Override     public void lockInterruptibly() throws InterruptedException {     }     Override     public Condition newCondition() {         return null;     } } import cn.hutool.core.util.IdUtil;import com.atguigu.redislock.mylock.DistributedLockFactory;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;Service Slf4jpublic class InventoryService {     Autowired     private StringRedisTemplate stringRedisTemplate;     Value(${server.port})     private String port;     Autowired     private DistributedLockFactory distributedLockFactory;     public String sale() {         String retMessage  ;         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             //1 查询库存信息             String result  stringRedisTemplate.opsForValue().get(inventory001);             //2 判断库存是否足够             Integer inventoryNumber result  null ? 0 : Integer.parseInt(result);             //3 扣减库存             if(inventoryNumber  0) {                 stringRedisTemplate.opsForValue().set(inventory001,String.valueOf(--inventoryNumber));                 retMessage  成功卖出一个商品库存剩余: inventoryNumber;                 System.out.println(retMessage);                //暂停几秒钟线程,为了测试自动续期                 try { TimeUnit.SECONDS.sleep(120); } catch (InterruptedException e) { e.printStackTrace(); }             }else{                 retMessage  商品卖完了o(╥﹏╥)o;             }         }catch (Exception e){             e.printStackTrace();         }finally {             redisLock.unlock();         }         return retMessage\t服务端口号port;     }     private void testReEnter() {         Lock redisLock  distributedLockFactory.getDistributedLock(redis);         redisLock.lock();         try {             System.out.println(################测试可重入锁####################################);         }finally {             redisLock.unlock();         }     } } 总结流程 1、使用 synchronized 关键字或使用jdk中Lock单机版OK上分布式死翅翘 2、nginx分布式微服务单机锁不行 3、取消单机锁上redis分布式锁setnx 3.1、只加了锁没有释放锁出异常的话可能无法释放锁必须要在代码层面finally释放锁 3.2、宕机了部署了微服务代码层面根本没有走到finally这块没办法保证解锁这个key没有被删除需要有lockKey的过期时间设定 4、为redis的分布式锁key增加过期时间此外还必须要setnx过期时间同一行 4.1、必须规定只能自己删除自己的锁你不能把别人的锁删除了防止张冠李戴1别22删3的情况 5、unlock变为lua脚本保证原子性 6、锁重入hset替代setnxlock变为Lua脚本保证 7、锁的自动续期
http://www.dnsts.com.cn/news/124490.html

相关文章:

  • 永久建站平台在网上怎么赚钱?
  • 百度站长工具排名工厂办公室简单装修
  • 做算法题的 网站网页制作与设计实验报告
  • 网站为什么要备案东莞网站建设营销服务平台
  • 网站制作网页版南宁网站建设建站系统
  • 网站最佳颜色搭配游戏网站建设的策划书
  • 做的网站怎样更新app拉新推广平台代理
  • 网站后台 用什么编写广西北海市住房和建设厅网站
  • 做一元购网站 要多少钱龙华做棋牌网站建设多少钱
  • 英文医疗网站建设dw和mysql做网站
  • 做冲压件加工有什么好网站建设网站带后台管理
  • 漳州城乡住房建设部网站运城网站推广
  • jsp做网站毕业设计婚庆公司网站php源码
  • 盐城网站建设官网先网站开发后软件开发
  • 在阿里云网站建设class wp wordpress
  • 保定网站建设冀icp备中国建设银行互联网站
  • 网站模板d一品资源网千万别在百度上搜别人的名字
  • 手机做wifi中继上外国网站网站子页怎么做 视频
  • 免费网站建设排行wordpress替换dede
  • php网站开发软件编程上海进出口贸易博览会
  • linux系统网站空间页面模板不包括
  • 网站建设脑图博客网站入口
  • 哪个网站可以找到毕业设计ui设计好学吗?要学多久
  • 诸城网站建设与制作wordpress 数据导入
  • 做网站的软件叫code国土资源和建设部网站
  • 网站建设任务分解开发直播app赚钱吗
  • 快速搭建网站的方法邓海舟网站建设教程
  • 3d做ppt模板下载网站广西桂林理工大学
  • 淘客cms建站域名 a记录 手机网站
  • 做视频网站需要哪些技术排名优化是什么意思