分布式锁主要用于分布式服务器保证共享资源的访问同步。
实现流程:先获取锁,得到锁可以访问共享资源。如:库存资源。
当前用户访问时先获取锁,才可以访问资源,如:
将抢购的商品id作为key,过期时间作为value,存到redis服务,
当锁没有释放时,其他用户访问同一资源时,获取不了锁则无法访问;
机制:锁未释放,用户访问相同资源,先会尝试获取锁。
获取锁机制:如果锁不存在则设置锁,存在则获取value1(过期时间)
如果未过期则获取锁失败,过期则重新设置锁并获取之前锁的过期时间value2
value1和value2比较相同则获取锁成功,反之失败。*****
过期时间比较是为了防止同时大量用户访问并设置了锁。
package com.gxuwz.spring.demo.common.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* redis分布式锁
*/
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 获取锁
*
* @param key
* @param timeStamp 过期时间戳
* @return
*/
public boolean lock(String key, String timeStamp){
// 如果不存在key则设置成功,即获取锁,如果key存在则什么都不做
if (redisTemplate.opsForValue().setIfAbsent(key, timeStamp)){
return true;
}
String time = redisTemplate.opsForValue().get(key);
// key存在但过期了
if (! StringUtils.isEmpty(time) && Long.parseLong(time) < System.currentTimeMillis()){
// 重新设置key,并获取之前的值
String beforeTime = redisTemplate.opsForValue().getAndSet(key, timeStamp);
// 只让当前设置key的线程获得锁
if (!StringUtils.isEmpty(beforeTime) && beforeTime.equals(time)){
return true;
}
}
// 获取锁失败
return false;
}
/**
* 释放锁
*
* @param key
* @param timeStamp
*/
public void release(String key, String timeStamp){
String time = redisTemplate.opsForValue().get(key);
try {
if (!StringUtils.isEmpty(time) && time.equals(timeStamp)) {
Boolean delete = redisTemplate.opsForValue().getOperations().delete(key);
System.out.println("删除标识:" + delete);
}
}catch (Exception e){
e.printStackTrace();
System.out.println("释放锁失败!");
}
}
}
业务层
@Autowired
private RedisLock redisLock;
/**
* 抢购商品(测试分布式锁)
*
* @param id
* @param count
* @return
*/
@Override
public String purchase(int id, int count) {
String key = id + "";
// 过期时间 = 当前时间戳 + 过期时间(秒)
long currentTimeMillis = System.currentTimeMillis() + TIMESTAMP;
// 获取锁
if(! redisLock.lock(key, String.valueOf(currentTimeMillis))){
return "对不起!购买人数太多";
}
try {
try{
// 为了更好的测试多线程同时进行库存扣减,在进行数据更新之后先等1秒,让多个线程同时竞争资源
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
Goods goods = repository.getGoodsById(id);
if (goods == null) { // 不存在该物品
return "购买物品失败";
} else if (goods.getCount() < count) {
return "对不起!库存余额不足";
}
int flag = repository.purchase(id, count);
System.out.println("flag:" + flag);
return "购买成功!";
}finally {
// 释放锁
redisLock.release(key, String.valueOf(currentTimeMillis));
System.out.println("释放锁的时间戳:"+ currentTimeMillis);
}
}
应用场景:多台服务器访问的共享资源。
网友评论