redis实现分布式锁(java版)

哈喽,大家好,我是花臂。今天给大家介绍如何使用redis实现分布式锁。

前提条件

首先,redis分布式锁要具备以下五点要求:
1.互斥性: 在任意一个时刻,只能有一个客户端持有锁
2.锁超时: 设置锁的超时时间,防止客户端崩溃没有释放锁导致死锁的情况
3.阻塞与非阻塞: 根据业务场景选用阻塞或者非阻塞方式
4.可重入: 在外层拿到锁的情况下在内层直接获取锁,无需重复拿锁
5.高可用:防止业务执行时间超过锁的超时时间,给锁续命

maven依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

这里我们采用SpringRedisTemplate模板来操作redis

代码实现

!获取锁

@Component
public class RedisUtils {

    /**
     * redis分布式锁:
     * 1.互斥性: 在任意一个时刻,只能有一个客户端持有锁
     * 2.锁超时: 设置锁的超时时间,防止客户端崩溃没有释放锁导致死锁的情况
     * 3.阻塞与非阻塞: 根据业务场景选用阻塞或者非阻塞方式
     * 4.可重入: 在外层拿到锁的情况下在内层直接获取锁,无需重复拿锁
     * 5.高可用:防止业务执行时间超过锁的超时时间,给锁续命
     */
    @Autowired
    private StringRedisTemplate redisTemplate;
    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    //防止线程覆盖uuid,使用threadlocal线程局部变量存放uuid(客户端标识)
    ThreadLocal<String> threadLocal = new ThreadLocal<>();

    /**
     * 加锁操作
     *
     * @param key      加锁的key
     * @param time     锁的超时时间
     * @param timeUnit 锁超时时间单位
     * @return
     */
    public Boolean tryLock(String key, Long time, TimeUnit timeUnit) {
        Boolean lock = false;
        //实现可重入锁
        if (threadLocal.get() == null) {
            //使用uuid作为每个客户端的标识,加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
            String value = UUID.randomUUID().toString();
            threadLocal.set(value);
            lock = redisTemplate.opsForValue().setIfAbsent(key, value, 10, timeUnit);
            //实现非阻塞式锁
            if (!lock) {
                //循环获取锁
                while (true) {
                 /*   System.out.println("循环获取锁");*/
                    lock = redisTemplate.opsForValue().setIfAbsent(key, value, 10, timeUnit);
                    if (lock) {
                        break;
                    }
                }
            }
            //异步实现锁续命,保证高可用
            executorService.execute(new Runnable() {
                @Override
                public void run() {

                    while (true) {
                        if (redisTemplate.opsForValue().get(key) != null) {
                            System.out.println("锁续命");
                            redisTemplate.expire(key, 10, TimeUnit.SECONDS);
                        } else {
                            break;
                        }
                        try {
                            Thread.sleep(5000); //每隔五秒延长一次锁的超时时间
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            });
        } else if (redisTemplate.opsForValue().get(key).equals(threadLocal.get())) {
            return true;
        }
        return lock;
    }

!释放锁

 /**
     * 释放锁
     */
    public void releaseLock(String key) {
        if (redisTemplate.opsForValue().get(key).equals(threadLocal.get())) {
            redisTemplate.delete(key);
            threadLocal.remove();//及时清空threadlocal,防止线程复用
        }

    }

ok,一个相对完美的分布式锁就实现了,其实现在比较成熟的框架比如redission,底层实现原理也是跟这一样的,就一个trylock和一个unlock方法就搞定所有了。

评论区



© [2020] · Powered by Typecho · Theme by Morecho
鄂ICP备20005123号