redis分布式锁

1.配置类

@Primary
@Bean("clusterObjectRedisTemplate")
public RedisTemplate<String, Object> objectTemplate(RedisConnectionFactory factory) {

    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    // key采用String的序列化方式
    template.setKeySerializer(new StringRedisSerializer());
    // value序列化方式采用jackson
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.afterPropertiesSet();
    return template;
}

2.获取锁

2.1 lua脚本

private static final String TRY_LOCK_SCRIPT =
        "if redis.call('SET',KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2]) " +
        "then " +
        "   return 1 " +
        "else" +
         "  return 0 " +
        "end";

2.2 获取锁

/**
 * 获取锁,获取锁成功返回true,获取失败返回false
 * @param lockKey  锁key值
 * @param identify 锁value值,通过value对比释放锁
 * @param expireMillisecond 过期时间
 * @return
 */
public Boolean lock(String lockKey, String identify, Long expireMillisecond) {
    try {``
        RedisScript<Long> redisScript = new DefaultRedisScript<>(TRY_LOCK_SCRIPT, Long.class);
        Object result = objectTemplate.execute(redisScript, Collections.singletonList(lockKey), identify, expireMillisecond);
        if (LOCK_SUCCESS.equals(result)) {
            return Boolean.TRUE;
        }
    } catch (Exception e) {
        logger.error("获取锁失败,lockKey=[{}], identify=[{}]", lockKey, identify, e);
    }
    return Boolean.FALSE;
}

2.3 尝试多次获取锁

/**
 * 尝试获取锁
 * @param key
 * @param value
 * @param expireMillisecond      过期时间
 * @param waitMillisecond 等待锁的时间
 */
public Boolean tryLock(String key,String value, Long expireMillisecond, Long waitMillisecond) {
    while(waitMillisecond > 0 ){
        Boolean lockFlag = lock(key, value, expireMillisecond);
        if(lockFlag) {
            return true;
        }else{
            try {
                Thread.sleep(INTERVAL_MILLISECOND);
            } catch (InterruptedException e) {
                logger.error("睡眠中断",e);
            }
            waitMillisecond = waitMillisecond - INTERVAL_MILLISECOND;
        }

    }
    return false;
}

3.释放锁

3.1 lua脚本

private static final String RELEASE_LOCK_SCRIPT =
        "if redis.call('get', KEYS[1]) == ARGV[1] " +
        "then " +
        "   return redis.call('del', KEYS[1]) " +
        "else " +
        "   return 0 " +
        "end";

3.2 释放锁

/**
 * 释放锁,释放成功返回true,释放失败返回false
 * @param lockKey 锁key值
 * @param identify 判断锁value是否相同,相同才释放锁
 * @return
 */
public Boolean unLock(String lockKey, String identify) {
    try {
        RedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT, Long.class);
        Object result = objectTemplate.execute(redisScript, Collections.singletonList(lockKey), identify);
        if (LOCK_SUCCESS.equals(result)) {
            return Boolean.TRUE;
        }
    } catch (Exception e) {
        logger.error("解锁失败,lockKey=[{}], identify=[{}]", lockKey, identify, e);
    }
    return Boolean.FALSE;
}

4.获取锁测试

@Test
public void testLock() throws InterruptedException {
    int threadCount = 4;
    CountDownLatch countDownLatch = new CountDownLatch(threadCount);
    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
    int j = 1;
    for (int i = 0; i < threadCount; i++) {
        executorService.submit(() -> {
            String uuid = UUIDUtil.getUUID();
            Boolean lock = redisUtil.tryLock("a12312312312dadq", uuid, 8 * 1000L,6* 1000L);
            if (lock) {
                try {
                    log.error("线程[{}]正在执行中,uuid:{}", Thread.currentThread().getName(), uuid);
                    try {
                        Thread.sleep(7000);
                        log.error("线程[{}]执行完毕", Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    boolean releaseResult = redisUtil.unLock("a12312312312dadq", uuid);
                    log.error("线程[{}]释放redis,uuid:{},释放状态:{}", Thread.currentThread().getName(), uuid, releaseResult);
                }
            } else {
                log.error("线程[{}]获取锁失败", Thread.currentThread().getName());
            }
            countDownLatch.countDown();
        }, j++);
    }
    countDownLatch.await();
}
posted @ 2022-07-18 17:28  雾里看花的少年  阅读(91)  评论(0编辑  收藏  举报