用redis实现简单的分布式锁

import com.zte.itp.msa.core.util.SpringContextUtil;
import org.jasypt.commons.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

public class RedisLock {
    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
    /* 默认锁的有效时间30s */
    private static final int DEFAULT_LOCK_EXPIRSE_MILL_SECONDS = 30 * 1000;
    /* 默认请求锁等待超时时间5s */
    private static final int DEFAULT_LOCK_WAIT_DEFAULT_TIME_OUT = 5 * 1000;
    /* 默认的轮询获取锁的间隔时间500ms */
    private static final int DEFAULT_LOOP_WAIT_TIME = 500;    /* 锁的key前缀 */
    private static final String LOCK_PREFIX = "LOCK_";
    /* 是否获得锁的标志 */    private boolean lock = false;
    /* 锁的key */    private String lockKey;    /* 锁的有效时间(ms) */
    private int lockExpirseTimeout;    /* 请求锁的阻塞时间(ms) */
    private int lockWaitTimeout;

    private static final RedisTemplate<String, Object> redisTemplate= (RedisTemplate<String, Object>) SpringContextUtil
            .getBean("redisTemplate");

    public boolean isLock() {
        return lock;
    }

    public void setLock(boolean lock) {
        this.lock = lock;
    }

    public String getLockKey() {
        return lockKey;
    }

    public void setLockKey(String lockKey) {
        this.lockKey = LOCK_PREFIX + lockKey;
    }

    public int getLockExpirseTimeout() {
        return lockExpirseTimeout;
    }

    public void setLockExpirseTimeout(int lockExpirseTimeout) {
        this.lockExpirseTimeout = lockExpirseTimeout;
    }

    public int getLockWaitTimeout() {
        return lockWaitTimeout;
    }

    public void setLockWaitTimeout(int lockWaitTimeout) {
        this.lockWaitTimeout = lockWaitTimeout;
    }


    public RedisLock() {
    }

    public RedisLock(String lockKey, int lockExpirseTimeout, int lockWaitTimeout) {
        this.lockKey =  lockKey;
        this.lockExpirseTimeout = lockExpirseTimeout;
        this.lockWaitTimeout = lockWaitTimeout;
    }

    public RedisLock newInstance(String lockKey) {
        RedisLock redisLock = new RedisLock(lockKey, DEFAULT_LOCK_EXPIRSE_MILL_SECONDS, DEFAULT_LOCK_WAIT_DEFAULT_TIME_OUT);
     //   redisLock.setRedisTemplate(this.redisTemplate);
        return redisLock;
    }

    public RedisLock newInstance(String lockKey, int lockExpirseTimeout, int lockWaitTimeout) {
        if (lockExpirseTimeout == 0 || lockWaitTimeout == 0) {
            lockExpirseTimeout = DEFAULT_LOCK_EXPIRSE_MILL_SECONDS;
            lockWaitTimeout = DEFAULT_LOCK_WAIT_DEFAULT_TIME_OUT;
        }
        RedisLock redisLock = new RedisLock(lockKey, lockExpirseTimeout, lockWaitTimeout);
       // redisLock.setRedisTemplate(this.redisTemplate);
        return redisLock;
    }

    public boolean setIfAbsent(String expirseTimeStr) {
        // setIfAbsent通过jedis的setNx实现
        return this.redisTemplate.opsForValue().setIfAbsent(this.lockKey, expirseTimeStr);
    }

    public String getAndSet(String expiresTimeStr) {
        // 获取原来的值,并设置新的值,原子操作
        return (String) this.redisTemplate.opsForValue().getAndSet(this.lockKey, expiresTimeStr);
    }

    /**
     * 1、获得当前系统时间,计算锁的到期时间     * 2、setNx操作,加锁
     * * 3、如果,加锁成功,设置锁的到期时间,返回true;取锁失败,取出当前锁的value(到期时间)
     * * 4、如果value不为空而且小于当前系统时间,进行getAndSet操作,重新设置value,并取出旧value;否则,等待间隔时间后,重复步骤2;     * 5、如果步骤3和4取出的value一样,加锁成功,设置锁的到期时间,返回true;否则,别人加锁成功,恢复锁的value,等待间隔时间后,重复步骤2。
     */
    public boolean lock() throws Exception{
        log.info("{}-----尝试获取锁...",
                Thread.currentThread().getName());
        int lockWaitMillSeconds = this.lockWaitTimeout;
        // key 的值,表示key的到期时间
        String redisValue = String.valueOf(System.currentTimeMillis() + this.lockExpirseTimeout);
        while (lockWaitMillSeconds > 0) {
            lock = setIfAbsent(redisValue);
            if (lock) {
                // 拿到锁,设置锁的有效期,这里可能因为故障没有被执行,锁会一直存在,这时就需要value的有效期去判断锁是否失效
                this.redisTemplate.expire(this.lockKey, lockExpirseTimeout, TimeUnit.MILLISECONDS);
                log.info("{}-----获得锁", Thread.currentThread().getName());
                return lock;
            } else {
                // 锁存在,判断锁有没有过期
                String oldValue = (String) this.redisTemplate.opsForValue().get(this.lockKey);
                if (CommonUtils.isNotEmpty(oldValue) && Long.parseLong(oldValue) < System.currentTimeMillis()) {
                    // 锁的到期时间小于当前时间,说明锁已失效, 修改value,获得锁
                    String currentRedisValue = getAndSet(String.valueOf(lockExpirseTimeout + System.currentTimeMillis()));
                    if (currentRedisValue.equals(oldValue)) {
                        // 如果修改的锁的有效期之前没被其他线程修改,则获得锁, 设置锁的超时时间
                        redisTemplate.expire(this.lockKey, lockExpirseTimeout, TimeUnit.MILLISECONDS);
                        log.info("{}-----获得锁", Thread.currentThread().getName());
                        this.lock = true;
                        return this.lock;
                    } else {                        // 有另外一个线程获得了这个超时的锁,不修改锁的value
                        redisTemplate.opsForValue().set(this.lockKey, currentRedisValue);
                    }
                }
            }            // 减掉固定轮询获取锁的间隔时间
            lockWaitMillSeconds -= DEFAULT_LOOP_WAIT_TIME;
            try {
                log.info("{}-----等待{}ms后,再尝试获取锁...",
                        Thread.currentThread().getName(), DEFAULT_LOOP_WAIT_TIME);
                // 取锁失败时,应该在随机延时后进行重试,避免不同客户端同时重试导致谁都无法拿到锁的情况出现,也可以采用等待队列的方式
                Thread.sleep(DEFAULT_LOOP_WAIT_TIME);
            } catch (InterruptedException e) {
                log.error("redis 同步锁出现未知异常", e);
            }
        }
        log.info("{}-----请求锁超时,获得锁失败", Thread.currentThread().getName());
        return false;
    }


    public void unlock() {
        if (lock) {
            this.redisTemplate.delete(this.lockKey);
            this.lock = false;
        }
    }
}




@Scheduled(cron="0 0 0/1 * * ?")
public void redisLock() throws Exception {
    System.out.println("==开始执行==");
    //redis分布式锁
    RedisLock lock = new RedisLock(Constants.REDIS_KEY_LOCK, 12*60*60 * 1000,
            10 * 1000);
    if (lock.lock()) {
        System.out.println("work1获得锁");
        long startTime = System.currentTimeMillis();   //获取开始时间
        try {
           
        }
        catch(Exception e){
            logger.info(e.getMessage(),e);
        }

        long endTime = System.currentTimeMillis(); //获取结束时间
        System.out.println("==结束执行==");
        Thread.sleep(10000);
        System.out.println("程序运行时间: " + (endTime - startTime) + "ms");
        lock.unlock();
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值