redis实现同步锁的一种实例

先定义lock对象

package com.huazhu.erp.commonbc.domain.lock;

import com.huazhu.base.utils.DateUtil;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.joda.time.DateTime;

import java.time.Duration;

@ToString
@EqualsAndHashCode
@Getter
public final class GlobalLock {

    private final String lockKey;

    private final String lockValue;

    private final Duration duration;

    private GlobalLock(String lockKey, Duration duration) {
        this.lockKey = lockKey;
        this.duration = duration;
        this.lockValue = DateTime.now().toString(DateUtil.COMMON_PATTERN_SS);
    }

    public static GlobalLock of(String lockKey, Duration duration) {
        return new GlobalLock(lockKey, duration);
    }
}

添加锁的实现方式。可用于避免方法被重复执行等

package com.huazhu.erp.commonbc.application;

import com.huazhu.base.utils.DateUtil;
import com.huazhu.erp.commonbc.domain.lock.GlobalLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Random;

@Slf4j
@Service
public class GlobalLockAppService {

    private RedisTemplate<String, String> redisTemplate;

    private final Random rand = new Random();

    /**
     * 获取锁
     */
    public boolean getLock(GlobalLock lock) {
        if (StringUtils.isEmpty(lock.getLockKey())
                || StringUtils.isEmpty(lock.getLockValue())) {
            return false;
        }
        return Boolean.TRUE.equals(
                redisTemplate.execute((RedisCallback<Boolean>) connection -> {
                    RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                    byte[] key = serializer.serialize(lock.getLockKey());
                    byte[] value = serializer.serialize(lock.getLockValue());
                    Boolean locked;
                    if (key == null || value == null) {
                        locked = false;
                    } else {
                        locked = connection.set(key,
                                value,
                                Expiration.from(lock.getDuration()), RedisStringCommands.SetOption.SET_IF_ABSENT);
                    }
                    log.info("getLock:{}:{}:{}:{}", locked, lock.getLockKey(), lock.getLockValue(), lock.getDuration());
                    return locked;
                })
        );
    }

    /**
     * 获取锁
     */
    public boolean tryLockMultipleTimes(GlobalLock lock) {
        for (int i = 0; i < 3; i++) {
            if (getLock(lock)) {
                return true;
            }
            try {
                int millis = rand.nextInt(200) + 1;
                Thread.sleep(millis);
            } catch (InterruptedException ex) {
                log.warn(ex.getMessage(), ex);
                Thread.currentThread().interrupt();
            }
        }
        log.info("加锁:{}-失败",lock.getLockKey());
        return false;
    }

	//lua脚本删除redis锁
    public void unlock(GlobalLock lock) {
        String luaScript = "local value = redis.call('get', KEYS[1]) if(value) then if(value == ARGV[1]) then return " +
                "redis.call('del', KEYS[1]) else  return -1 end else  return 0  end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
        Long execute = redisTemplate.execute(redisScript, Collections.singletonList(lock.getLockKey()),
                lock.getLockValue());
        String releaseTime = DateTime.now().toString(DateUtil.COMMON_PATTERN_SS);
        log.info("lock-close:{}:{}:releaseTime:{}:delFlag:{}",
                lock.getLockKey(), lock.getLockValue(), releaseTime, execute);
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
}

使用实例:

//lockKey是此次方法需要加锁的对象,通常是主键或者单号之类的,能够区分请求的关键信息
GlobalLock globalLock = GlobalLock.of(lockKey, Duration.ofSeconds(30L));
//如果没有获取锁则不做处理,只有获取了锁之后才能做业务操作
 if (!globalLockAppService.getLock(globalLock)) {
       log.warn("RepeatSupplierSync:{}",supplierVO.getSupplierName());
} else {
	//执行业务代码
} finally {
	//释放锁
    globalLockAppService.unlock(globalLock);
}
  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值