页面设计制作网站wordpress有商城吗
web/
2025/10/1 19:26:24/
文章来源:
页面设计制作网站,wordpress有商城吗,seo怎么去优化网站,芜湖网站建设芜湖在分布式系统中#xff0c;如果使用JVM中的同步锁在高并发的场景下仍然会产生线程安全问题。首先我们来查看在多个服务器时为什么会产生线程安全问题#xff0c;有这样一个案例#xff0c;有一件商品购买规则为一个用户只能购买一次#xff0c;如果使用同步锁锁住用户id如果使用JVM中的同步锁在高并发的场景下仍然会产生线程安全问题。首先我们来查看在多个服务器时为什么会产生线程安全问题有这样一个案例有一件商品购买规则为一个用户只能购买一次如果使用同步锁锁住用户id只能保证在一个服务器中一个用户只能购买一次在集群模式下就可能产生并发问题。 为了避免这个问题我们应该采取一个新的锁监视器当需要加锁时所有服务器都需要从外部的锁监视器中查看是否有线程加锁如果没有则获取互斥锁如果已经有线程获取到互斥锁那么就阻塞等待。模型图如下 什么是分布式锁
满足分布式系统或集群模式下多进程可见并互斥的锁。
分布式锁的实现
分布式锁的核心是实现多进程之间的互斥常见的实现方式有三种 MySQL Redis Zookeeper 互斥 利用MySQL本身的互斥锁机制 利用setnx这样的互斥命令 利用节点的唯一性和有序性实现互斥 高可用 好 好 好 高性能 一般 好 一般 安全性 断开连接自动释放锁 利用锁超时时间到期释放 临时节点断开连接自动释放
这里我们介绍Redis的实现方式首先是需要实现的两个最基本的方法 获取锁通过setnx命令并expire命令设置超时时间。 释放锁通过del命令或是宕机后通过超时时间释放。 但是在获取锁时可能会存在一个问题那就是在setnx时执行成功但是在expire时宕机没设置到超时时间为了避免这种情况我们需要保证两个命令的原子性可以采用lua脚本又或是采用set方法指定ex与nx参数采用set语法如下
set lock 1 nx ex 10 在Java中实现代码如下 public class SimpleRedisLock{private String name;private StringRedisTemplate stringRedisTemplate;private static final String key_Prefixlock:;public SimpleRedisLock(String name,StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean getLock(String key,long timeOut) {String id Thread.currentThread().getId();//因为这里Redis会返回一个Boolean类型但是结果要boolean要进行拆箱如果没查到的话会返回一个null直接返回结果容易造成空指针异常Boolean success stringRedisTemplate.opsForValue().setIfAbsent(key_Prefix key, id , timeOut, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void rmLock(String key) {stringRedisTemplate.delete(key_Prefixkey);}
}
但是这样又存在一个问题那就是如果一个获取到锁的线程因为某些原因阻塞导致已经超过了锁的超时时间还没有执行完毕此时如果新的线程来获取锁因为Redis已经将锁删除了因此可以顺利获取到锁。在第二个线程正在执行业务时第一个线程执行完毕开始执行删除锁操作按照我们所实现的代码会将第二个线程的锁删除此时第三个线程它也可以开始获取到新的锁然后在执行期间锁被第二个线程释放。从而造成并行错误。具体模型图如下 为了解决这个问题我们需要修改初始代码如下正确的模型图如下 public class SimpleRedisLock{private StringRedisTemplate stringRedisTemplate;private static final String key_Prefixlock:;//修改线程标识为UUIDtoString方法中的true是为了将UUID中的-去除我们需要自己在后面拼接一个-private static final String ID_Prefix UUID.randomUUID().toString(true)-;public SimpleRedisLock(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean getLock(String key,long timeOut) {String id ID_PrefixThread.currentThread().getId();//因为这里Redis如果没查到的话会返回一个null类型是Boolean但是结果要boolean要进行拆箱boolean只又true以及false会报空指针异常Boolean success stringRedisTemplate.opsForValue().setIfAbsent(key_Prefix key, id , timeOut, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void rmLock(String key) {//获取锁标识。如果相同在释放不同则什么也不做String id ID_PrefixThread.currentThread().getId();String value stringRedisTemplate.opsForValue().get(key_Prefix key);if (id.equals(value)) stringRedisTemplate.delete(key_Prefixkey);}
} 但是这样也无法完全解决分布式锁可能产生的问题接下来我们查看另一种模型图 线程一在判断过Redis中锁标识一样后就在开始执行释放锁时比如说开始gc垃圾回收或是其他原因导致的暂时阻塞而在阻塞期间线程一的锁标识超时释放这时线程二进行获取锁。线程一阻塞结束由于要执行删除操作再次把线程二的锁误删。
这样我们如果通过修改Java代码来解决该问题就过于复杂需要依赖Redis中的事务机制以及乐观锁实现因此更推荐使用Lua脚本来保证获取锁与删除锁的原子性。
简单介绍一下Redis中在Lua语言中提供调用的方法
redis.call(命令名称,key,其他参数)-- 比如说要执行set name 张三
redis.call(set,name,张三)-- 如果不想写死需要执行keyvalue那么可以通过参数传递
-- key 类型会放在KEYS数组当中。value会放在ARGV数组当中
-- 需要注意的时Lua语言中数组下标以1开始
redis.call(set,KEYS[1],ARGV[1])
RedisTemplate调用lua脚本的API如下
redistemplate.excute(script,keys,args); 再次修改Java代码如下 public class SimpleRedisLock{private StringRedisTemplate stringRedisTemplate;private static final String key_Prefixlock:;private static final String ID_Prefix UUID.randomUUID().toString(true)-;private static final DefaultRedisScriptLong rmLock_script;static {rmLock_scriptnew DefaultRedisScript();//设置脚本位置rmLock_script.setLocation(new ClassPathResource(rmlock.lua));//设置返回类型rmLock_script.setResultType(Long.class);}public SimpleRedisLock(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean getLock(String key,long timeOut) {String id ID_PrefixThread.currentThread().getId();//因为这里Redis如果没查到的话会返回一个null类型是Boolean但是结果要boolean要进行拆箱boolean只又true以及false会报空指针异常Boolean success stringRedisTemplate.opsForValue().setIfAbsent(key_Prefix key, id , timeOut, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}Overridepublic void rmLock(String key) {//调用lua脚本stringRedisTemplate.execute(rmLock_script, Collections.singletonList(key_Prefix key),ID_PrefixThread.currentThread().getId());}} 具体的执行脚本代码如下 if(redis.call(get,KEYS[1]) ARGV[1]) thenreturn redis.call(del,KEYS[1])
end
return 0
这样的实现方式已经可以避免普通的并发问题但是仍然存在一定问题比如说存在一个业务需要方法A调用方法B而在这两个方法中需要获取同一把锁那么就是产生死锁问题因此我们还需要实现锁可重入。其次我们的实现方式中如果没有获取到锁会立即返回但是通常我们需要进行重试我们还需要实现重试机制。还有主从不一致问题这些问题让我们实际开发中实际并不现实因此我们可以选择Redisson来解决以上问题。
Redisson的简单使用 在pom文件引入依赖 dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.19.0/version
/dependency
配置Redis我们可以选择yaml文件配置也可以选择Java配置类
Configuration
public class RedisConfig {Beanpublic RedissonClient redissonClient(){//配置类Config confignew Config();config.useSingleServer().setAddress(redis://127.0.0.1:6379);return Redisson.create(config);}
}
Redisson的简单使用
SpringBootTest
class RedissonApplicationTests {Resourceprivate RedissonClient redissonClient;Testpublic void test01() throws Exception {//获取锁指定锁名称可重入RLock lock redissonClient.getLock(lock);//三个参数分别是最大获取锁等待时间期间会重试锁自动释放时间时间单位boolean flag lock.tryLock(1, 10, TimeUnit.SECONDS);if (flag){try{System.out.println(获取锁成功);}finally {lock.unlock();}}}
} 锁在Redis中存储结构如下其中value代表的是锁重入次数
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/85218.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!