常用的锁有单体应用的简单锁synchronize,但是遇到分布式部署的项目时就会在大并发下出现安全问题,数据出现脏数据,此时可以考虑使用redis分布式锁,或者zookeeper锁
在实际开发中集群部署会出现的各种情况都会造成数据不安全,比如秒杀的库存等,或者服务器宕机或者重启,或者节点挂掉,这里使用redis分布式锁来实现锁机制
第一步:导入redission依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.4</version>
</dependency>
第二步:封装CacheService常用方法类
package com.bigdata.hive.utlis;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RDeque;
import org.redisson.api.RList;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RQueue;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RSet;
import org.redisson.api.RSortedSet;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CacheService {
private final RedissonClient redissonClient;
@Autowired
public CacheService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public long deleteByPattern(String pattern) {
return this.redissonClient.getKeys().deleteByPattern(pattern);
}
public boolean exists(String key) {
return this.redissonClient.getKeys().countExists(new String[]{key}) == 1L;
}
public <T> RBucket<T> getRBucket(String key) {
return this.redissonClient.getBucket(key);
}
public RLock getLock(String key) {
return this.redissonClient.getLock(key);
}
public RReadWriteLock getRWLock(String key) {
return this.redissonClient.getReadWriteLock(key);
}
public <K, V> RMap<K, V> getRMap(String key) {
return this.redissonClient.getMap(key);
}
public <V> RSortedSet<V> getRSortedSet(String key) {
return this.redissonClient.getSortedSet(key);
}
public <V> RSet<V> getRSet(String key) {
return this.redissonClient.getSet(key);
}
public <V> RList<V> getRList(String key) {
return this.redissonClient.getList(key);
}
public <V> RQueue<V> getRQueue(String key) {
return this.redissonClient.getQueue(key);
}
public <V> RDeque<V> getRDeque(String key) {
return this.redissonClient.getDeque(key);
}
public RAtomicLong getRAtomicLong(String key) {
return this.redissonClient.getAtomicLong(key);
}
public RCountDownLatch getRCountDownLatch(String key) {
return this.redissonClient.getCountDownLatch(key);
}
public RTopic getRTopic(String key) {
return this.redissonClient.getTopic(key);
}
}
第三步:注入CacheService使用redis分布式锁
@Autowired
private CacheService cacheService;
实例:
package com.bigdata.hive.controller;
import com.bigdata.hive.utlis.CacheService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
*@author liuxingying
*@description
*@date 2020/9/9
*/
public class RedisLockDemo {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private CacheService cacheService;
@Autowired
private Redisson redisson;
/** @description 常规分布式redis加锁
* @params [shopCode]
* @return java.lang.String
* @author lxy
* @date 2020/9/9 17:08
**/
public String redisLock(String shopCode){
//防止高并发下一个线程的锁被另一个线程给释放了,就造成锁一直加了就被释放导致锁失效
String clientId = UUID.randomUUID().toString();
try {
//给redis加锁 setIfAbsent指shopCode存在的话就设置不了会返回false 不存在就设置成功返回true 从而实现上锁的效果
//防止服务器宕机但是又给shopCode+"lock"设置成功了但释放不了,加一个过期时间10秒
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(shopCode+"lock", clientId,10, TimeUnit.SECONDS);
if (!flag){
//说明同时访问了,加了锁
return "手速过快,请重新秒杀";
}
//加了锁就开始实现秒杀的业务 取出商品的数量
Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
if (num>0){
int stockNum = num -1;
stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
System.out.println("扣减成功,剩余库存:"+stockNum+"");
}else {
System.out.println("扣减失败,库存不足");
}
}finally {
//判断线程的加锁和释放锁是同一线程防止被其他并发下的释放 导致的锁失效
if (clientId.equals(stringRedisTemplate.opsForValue().get(shopCode+"lock"))){
//最后要删除掉shopCode,相当于释放锁 不然除了第一单之外都进不去
stringRedisTemplate.delete(shopCode);
}
}
return "end";
}
/** @description redission分布式加锁
* @params [shopCode]
* @return java.lang.String
* @author lxy
* @date 2020/9/9 17:08
**/
public String redission(String shopCode){
//redisson加锁 底层和上面的一样
RLock redissonLock = redisson.getLock(shopCode + "lock");
try {
//锁30秒
redissonLock.lock(30,TimeUnit.SECONDS);
Boolean isLockSuccess = redissonLock.tryLock();
if (!isLockSuccess){
//说明同时访问了,加了锁
return "手速过快,请重新秒杀";
}
//加了锁就开始实现秒杀的业务 取出商品的数量
Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
if (num>0){
int stockNum = num -1;
stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
System.out.println("扣减成功,剩余库存:"+stockNum+"");
}else {
System.out.println("扣减失败,库存不足");
}
}finally {
//解锁
redissonLock.unlock();
}
return "end";
}
/** @description redission分布式加锁 封装CacheService方法类
* @params [shopCode]
* @return java.lang.String
* @author lxy
* @date 2020/9/9 17:08
**/
public String cacheServiceDemo(String shopCode){
RLock rLock =null;
try {
boolean isLockSuccess = false;
try {
rLock = cacheService.getLock(shopCode + "lock");
isLockSuccess = rLock.tryLock();
} catch (Exception e) {
e.printStackTrace();
}
if (!isLockSuccess){
//说明同时访问了,加了锁
return "手速过快,请重新秒杀";
}
//加了锁就开始实现秒杀的业务 取出商品的数量
Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
if (num>0){
int stockNum = num -1;
stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
System.out.println("扣减成功,剩余库存:"+stockNum+"");
}else {
System.out.println("扣减失败,库存不足");
}
}finally {
//判断线程的加锁和释放锁是同一线程防止被其他并发下的释放 导致的锁失效
if (null != rLock && rLock.isLocked() && rLock.isHeldByCurrentThread()) {
//解锁
rLock.unlock();
}
}
return "end";
}
}