package com.xx.xxx.common.distributedLock;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* @author?
* @ClassName: RedisDistributedLock
* @Description:
* @date 2023/12/14 11:10
*/
public class RedisDistributedLockimplements Lock {
private StringRedisTemplatestringRedisTemplate;
? ? private StringlockName;//KEYS[1]
? ? private Stringuuid;//ARGV[1]
? ? private long timeout;//ARGV[2]
? ? @Autowired
? ? public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName, String uuid){
this.stringRedisTemplate = stringRedisTemplate;
? ? ? ? this.lockName = lockName;
? ? ? ? this.uuid = uuid + Thread.currentThread().getId();
? ? ? ? this.timeout =30L;
? ? }
@Override
? ? public void lock() {
tryLock();
? ? }
@Override
? ? public void lockInterruptibly()throws InterruptedException {
}
@Override
? ? public boolean tryLock() {
try {
tryLock(-1L, TimeUnit.SECONDS);
? ? ? ? }catch (InterruptedException e) {
throw new RuntimeException(e);
? ? ? ? }
return false;
? ? }
@Override
? ? public boolean tryLock(long time, @NotNull TimeUnit unit)throws InterruptedException {
if(-1 == 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";
? ? ? ? ? ? //加锁
? ? ? ? ? ? while (Boolean.FALSE.equals(stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), this.uuid, String.valueOf(this.timeout)))){
try{
TimeUnit.SECONDS.sleep(60L);
? ? ? ? ? ? ? ? }catch (InterruptedException e){
e.printStackTrace();
? ? ? ? ? ? ? ? }
}
// 加锁成功之后,新建一个后台扫描程序
? ? ? ? ? ? resetExpire();
? ? ? ? ? ? System.out.println("lockName:" +lockName +"\t" +"uuid:" +uuid);
return true;
? ? ? ? }
return false;
? ? }
@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("unlock lockName:" +lockName +"\t" +"uuid:" +uuid);
? ? ? ? Long flag =stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
? ? ? ? ? ? ? ? Arrays.asList(lockName), uuid);
? ? ? ? if(null == flag) {
throw new RuntimeException("this lock does not exists");
? ? ? ? }
}
@NotNull
@Override
? ? public ConditionnewCondition() {
return null;
? ? }
//自动续期
? ? private void resetExpire() {
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),
? ? ? ? ? ? ? ? ? ? ? ? uuid,
? ? ? ? ? ? ? ? ? ? ? ? String.valueOf(timeout)
)){
System.out.println("resetExpire lockName:" +lockName +"uuid:" +uuid);
? ? ? ? ? ? ? ? }
}
}, this.timeout/3, 100);
? ? }
}
package com.xx.xx.common.distributedLock;
import cn.hutool.core.util.IdUtil;
import lombok.AllArgsConstructor;
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;
/**
* @author xx
* @ClassName: DistributedLockFactory
* @Description:
* @date 2023/12/14 14:42
*/
@Component
public class DistributedLockFactory {
private StringRedisTemplatestringRedisTemplate;
? ? private Stringuuid;
? ? public DistributedLockFactory(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
? ? ? ? this.uuid = IdUtil.simpleUUID();
? ? }
public LockgetDistributedLock(String lockType, String lockName) {
if(lockType ==null){
return null;
? ? ? ? }
if("REDIS".equalsIgnoreCase(lockType)){
return new RedisDistributedLock(stringRedisTemplate, lockName, uuid);
? ? ? ? }else if ("ZK".equalsIgnoreCase(lockType)){
}
return null;
? ? }
}
package com.xxx.xxx.customer.service.impl;
import cn.hutool.core.util.IdUtil;
import com.xxx.xxx.common.distributedLock.DistributedLockFactory;
import com.xxx.xxx.common.distributedLock.RedisDistributedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author xxx
* @ClassName: InventoryService
* @Description:
* @date 2023/12/13 16:28
*/
@Service
public class InventoryService {
private final StringRedisTemplatestringRedisTemplate;
? ? private DistributedLockFactorydistributedLockFactory;
? ? private Locklock =new ReentrantLock();
? ? public InventoryService(StringRedisTemplate stringRedisTemplate, DistributedLockFactory distributedLockFactory) {
this.stringRedisTemplate = stringRedisTemplate;
? ? ? ? this.distributedLockFactory = distributedLockFactory;
? ? }
public Stringsale(){
System.out.println("InventoryService:" +stringRedisTemplate);
//? ? ? ? Lock redisDistributedLock = new RedisDistributedLock(stringRedisTemplate, "luojiaRedisLock");
? ? ? ? Lock redisDistributedLock =distributedLockFactory.getDistributedLock("REDIS", "luojiaRedisLock");
? ? ? ? String resMessage ="";
? ? ? ? redisDistributedLock.lock();
? ? ? ? try {
// 1查询库存
? ? ? ? ? ? String result =stringRedisTemplate.opsForValue().get("stock");
? ? ? ? ? ? Thread.sleep(35000);
? ? ? ? ? ? // 2 判断库存是否足够
? ? ? ? ? ? Integer inventoryNum = result ==null ?0 : Integer.parseInt(result);
? ? ? ? ? ? // 3 扣减库存,每次减少一个
? ? ? ? ? ? if(inventoryNum >0){
stringRedisTemplate.opsForValue().set("stock", String.valueOf(--inventoryNum));
//? ? ? ? ? ? ? ? testEntryRetry();
? ? ? ? ? ? }else {
System.out.println("sold out");
? ? ? ? ? ? }
}catch (InterruptedException e) {
throw new RuntimeException(e);
? ? ? ? }finally {
redisDistributedLock.unlock();
? ? ? ? }
return resMessage;
? ? }
private void testEntryRetry(){
Lock redisLock =distributedLockFactory.getDistributedLock("REDIS", "luojiaRedisLock");
? ? ? ? redisLock.lock();
? ? ? ? try {
System.out.println("测试可重入锁");
? ? ? ? }finally {
redisLock.unlock();
? ? ? ? }
}
}