使用redis创建自己的分布式锁

作者 QQ 1135409377 欢迎加Q 交流

RedisLock 简介

1:支付公平锁,非公平锁(只是在单机器范围内)
2:不支持锁的重入(即 同一个线程,不能获取同一把锁两次及以上)
3:一个redis的key对应一个锁
4:参数可配置
5:可以像 使用 java.util.concurrent.locks.ReentrantLock 一样使用。

RedisLock 方法介绍

不可中断,直到获取锁为止
lock(String key, int keyExpireSecond)

如果当前线程未被中断,则直到获取锁为止
lockInterruptibly(String key, int keyExpireSecond) throws InterruptedException

如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
tryLock(String key, int keyExpireSecond)

如果锁可用,则此方法将立即返回值 true。如果锁不可用,出于线程调度目的,将挂起当前线程,如果在线程挂起期间中断线程,则排除 InterruptedException
tryLock(String key, int keyExpireSecond, long timeout, TimeUnit unit)

RedisLock 使用演示

注意:演示的demo中使用的redisClient,是我们公司自己把redis给封装了。
需要使用者把 RedisLock 的 属性 redisClient 的对象类型修改为 自己的 redis 客户端的对象类型,列入 jedis,并且 spring xml 中,<constructor-arg name="redisClient" ref="r2mClusterClient"/>
把使用者自己的 redis 客户端的对象类型,注入到 redisClient 中。

spring xml 配置
<bean id="zkProvider" class="com.wangyin.rediscluster.provider.ZkProvider">
        <constructor-arg name="appName" value="QWE"/> <!--请修改为自己的appName-->
        <constructor-arg name="zkConnectionStr" value="178.28.48.208:2181,178.28.48.228:2181,178.28.48.248:2181"/>  <!--请修改为所需要使用环境的地址-->
        <constructor-arg name="zkTimeout" value="25000"/>
    </bean>

    <!--连接池配置, 这里连接配置指的是所有主节点得配置,指的是每一个主节点连接数,其他相关参数参看ApacheCommonPool-->
    <bean id="redisPoolConfig" class="com.wangyin.r2m.client.jedis.JedisPoolConfig">
        <property name="blockWhenExhausted" value="true"/> <!--连接池耗尽时是否阻塞-->
        <property name="maxWaitMillis" value="500" />
        <property name="maxTotal" value="15"/> <!---最大连接数,如果blockWhenExhausted为false会阻塞,为true会抛出异常-->
        <property name="maxIdle" value="10"/>  <!--最大可以保的空闲连接数量,实际空闲连接数会在 maxIdle与minIdle 之间-->
        <property name="minIdle" value="0"/> <!-- 最小空闲保持连接数,避免频繁创建-->
    </bean>

    <bean id="cacheClusterConfigProvider" class="com.wangyin.rediscluster.provider.CacheClusterConfigProvider">
        <property name="providers">
            <list>
                <ref bean="zkProvider"/>
            </list>
        </property>
    </bean>

    <bean id="r2mClusterClient" class="com.wangyin.rediscluster.client.R2mClusterClient">
        <property name="maxRedirections" value="3"/>
        <property name="redisTimeOut" value="500"/>
        <property name="redisPoolConfig" ref="redisPoolConfig"/>
        <property name="provider" ref="cacheClusterConfigProvider"/>
    </bean>
    <bean id="redisLock" class="com.lp.redis.lock.RedisLock">
        <!-- redis客户端 ,用户配置的时候,替换为自己的 redis客户端实例-->
        <constructor-arg name="redisClient" ref="r2mClusterClient"/>
        <!-- true 公平锁, false 非公平锁 ,默认值 false -->
        <constructor-arg name="fair" value="true"/>
        <!-- 尝试获取多少次锁后,如果还没有获取锁则挂起线程,默认值 2 -->
        <constructor-arg name="blockingAfterLockNum" value="5"/>
        <!-- 线程挂起时间,默认值 10毫秒 -->
        <constructor-arg name="parkThreadMilliscond" value="50"/>
     </bean>

测试代码
package com.lp.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.lp.redis.lock.RedisLock;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        final String redisKey = "keyrs";//redis key
        final int keyExpireSecond = 5;//redis key值的有效期 单位秒
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        final RedisLock redisLock = ctx.getBean("RedisLock", RedisLock.class);
        int size = 321;
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=1; i<=size; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    redisLock.lock(redisKey, keyExpireSecond);
                    //执行业务逻辑
                    System.out.println(MySource.getAndDecrement());
                    //
                    redisLock.unlock(redisKey);
                }
            });
        }

        while(true) {
            Thread.sleep(1000);
            //打印调试信息
            System.out.println(redisLock.getContentOfKeyLockMap(redisKey));
        }
    }
}

RedisLock 代码

package com.lp.redis.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

import com.wangyin.rediscluster.client.R2mClusterClient;

public class RedisLock {

    private ReentrantLock globalLock;
    /**
     * redis的一个key,对应一个 RedisLockThread
     */
    private Map<String, RedisLockThread> keyLockMap = new HashMap<String, RedisLockThread>();

    private R2mClusterClient redisClient;

    /**
     * 默认值 false
     * true,公平锁; false,非公平锁
     */
    private boolean fair;

    /**
     * 尝试获取多少次锁后,如果还没有获取锁则挂起线程,默认值 2
     */
    private int blockingAfterLockNum = 2;

    /**
     * 参数只对方法 lock,lockInterruptibly 有效
     * 线程挂起时间,默认值 10毫秒
     */
    private long parkThreadMilliscond = 10;

    /**
     * 挂起线程的纳秒数
     */
    private long parkThreadNano = parkThreadMilliscond * 1000 * 1000;

    public RedisLock(R2mClusterClient redisClient) {
        this.redisClient = redisClient;
        this.globalLock = new ReentrantLock(fair);
    }

    public RedisLock(boolean fair, int blockingAfterLockNum, long parkThreadMilliscond, R2mClusterClient redisClient) {
        this.fair = fair;
        this.blockingAfterLockNum = blockingAfterLockNum;
        this.parkThreadMilliscond = parkThreadMilliscond;
        this.parkThreadNano = parkThreadMilliscond * 1000 * 1000;
        this.redisClient = redisClient;
        this.globalLock = new ReentrantLock(fair);
    }

    public boolean isFair() {
        return fair;
    }

    public R2mClusterClient getRedisClient() {
        return redisClient;
    }

    public int getBlockingAfterLockNum() {
        return blockingAfterLockNum;
    }

    public long getParkThreadMilliscond() {
        return parkThreadMilliscond;
    }

    /**
     * 不可中断,直到获取锁为止
     * @param key redis的key值
     * @param keyExpireSecond redis key的有效期
     */
    public void lock(String key, int keyExpireSecond) {
        if (isBlank(key) || keyExpireSecond < 0) {
            throw new IllegalArgumentException("param is illegal");
        }
        boolean isSaveLock = false;
        RedisLockThread lockThread = null;
        if(fair) {//公平锁
            lockThread = saveLock(key);//保证一个key对应一个lock
            lockThread.getLock().lock();//线程获取锁
            isSaveLock = true;
        }
        int doNum = 0;
        do {
            long setNx = redisClient.setnx(key, String.valueOf(System.currentTimeMillis()));
            if (setNx == 1) {
                redisClient.expire(key, keyExpireSecond);
                return;
            } else {
                if(!isSaveLock) {
                    lockThread = saveLock(key);//保证一个key对应一个lock
                    lockThread.getLock().lock();//线程获取锁
                    isSaveLock = true;
                }
                doNum ++;
                if(doNum == blockingAfterLockNum) {//获取锁几次后,让然获取不到锁,则挂起线程指定的时间。
                    doNum = 0;
                    LockSupport.parkNanos(Thread.currentThread(), parkThreadNano);
                }
            }
        } while (true);
    }

    /**
     * 如果当前线程未被中断,则直到获取锁为止
     * @param key redis的key值
     * @param keyExpireSecond redis key的有效期
     * @throws InterruptedException
     */
    public void lockInterruptibly(String key, int keyExpireSecond) throws InterruptedException  {
        if (isBlank(key) || keyExpireSecond < 0) {
            throw new IllegalArgumentException("param is illegal");
        }
        boolean isSaveLock = false;
        RedisLockThread lockThread = null;
        if(fair) {//公平锁
            lockThread = saveLockInterruptibly(key);//保证一个key对应一个lock
            lockThread.getLock().lockInterruptibly();//线程获取锁
            isSaveLock = true;
        }
        int doNum = 0;
        do {
            long setNx = redisClient.setnx(key, String.valueOf(System.currentTimeMillis()));
            if (setNx == 1) {
                redisClient.expire(key, keyExpireSecond);
                return;
            } else {
                if(!isSaveLock) {
                    lockThread = saveLockInterruptibly(key);//保证一个key对应一个lock
                    lockThread.getLock().lockInterruptibly();//线程获取锁
                    isSaveLock = true;
                }
                doNum ++;
                if(doNum == blockingAfterLockNum) {//获取锁几次后,让然获取不到锁,则挂起线程指定的时间。
                    doNum = 0;
                    LockSupport.parkNanos(Thread.currentThread(), parkThreadNano);
                    if(Thread.interrupted()) {
                        throw new InterruptedException();
                    }
                }
            }
        } while (true);
    }

    /**
     * 如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
     * @param key redis的key值
     * @param keyExpireSecond redis key的有效期
     * @return 如果获取了锁,则返回 true 否则返回 false。 
     */
    public boolean tryLock(String key, int keyExpireSecond) {
        if (isBlank(key) || keyExpireSecond < 0) {
            throw new IllegalArgumentException("param is illegal");
        }
        long setNx = redisClient.setnx(key, String.valueOf(System.currentTimeMillis()));
        if (setNx == 1) {
            redisClient.expire(key, keyExpireSecond);
            return true;
        }
        return false;
    }

    /**
     * 如果锁可用,则此方法将立即返回值 true。
     * 如果锁不可用,出于线程调度目的,将挂起当前线程,如果在线程挂起期间中断线程,则排除 InterruptedException
     * @param key redis的key值
     * @param keyExpireSecond redis key的有效期
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public boolean tryLock(String key, int keyExpireSecond, long timeout, TimeUnit unit) throws InterruptedException {
        long lastTime = System.nanoTime();
        long nanosTimeout = unit.toNanos(timeout);
        if (isBlank(key) || keyExpireSecond < 0) {
            throw new IllegalArgumentException("param is illegal");
        }
        //
        boolean isSaveLock = false;
        RedisLockThread lockThread = null;
        if(fair) {//公平锁
            lockThread = saveLockInterruptibly(key);
            boolean isLock = lockThread.getLock().tryLock(timeout, unit);
            if(!isLock) {//没有获取锁
                lockThread.removeThreadOfLock(Thread.currentThread());//删除lock对应的线程
                return isLock;
            } else {
                isSaveLock = true;
            }
        }
        //
        int doNum = 0;//do{}while 循环多少次挂起线程
        do {
            long setNx = redisClient.setnx(key, String.valueOf(System.currentTimeMillis()));
            if (setNx == 1) {
                redisClient.expire(key, keyExpireSecond);
                return true;
            } else {
                if(!isSaveLock) {//非公平锁
                    lockThread = saveLockInterruptibly(key);
                    boolean isLock = lockThread.getLock().tryLock(timeout, unit);
                    if(!isLock) {
                        lockThread.removeThreadOfLock(Thread.currentThread());//删除lock对应的线程
                        return isLock;
                    } else {
                        isSaveLock = true;
                    }
                }
                //超时
                if (nanosTimeout <= 0) {
                    lockThread.removeThreadOfLock(Thread.currentThread());//删除lock对应的线程
                    lockThread.getLock().unlock();//lock释放锁
                    //删除 key 对应的锁
                    globalLock.lock();
                    if(lockThread.getThreadsOfLock() == 0) {//lock对应的线程数为 0,从集合中删除 key对应的lock。
                        keyLockMap.remove(key);
                    }
                    globalLock.unlock();
                    return false;
                }
                //循环获取锁
                doNum ++;
                if(doNum == blockingAfterLockNum) {
                    doNum = 0;
                    long now = System.nanoTime();
                    nanosTimeout -= now - lastTime;
                    LockSupport.parkNanos(Thread.currentThread(), nanosTimeout);
                    if (Thread.interrupted()) {
                        throw new InterruptedException();
                    }
                }
            }
        } while (true);
    }

    /**
     * 释放锁
     * @param key redis的key值
     */
    public void unlock(String key) {
        globalLock.lock();
        redisClient.del(key);//必须先删除redis的key
        RedisLockThread lockThread = keyLockMap.get(key);//获取key对应的lock
        if(lockThread != null) {
            if ( Thread.currentThread() != lockThread.getThreadOfLock(Thread.currentThread()) ) {//当前线程是否在,lock对应的线程集合内
                throw new IllegalMonitorStateException();
            } else {
                lockThread.removeThreadOfLock(Thread.currentThread());//删除lock对应的线程
                lockThread.getLock().unlock();//lock释放锁
                if(lockThread.getThreadsOfLock() == 0) {//lock对应的线程数为 0,从集合中删除 key对应的lock。
                    keyLockMap.remove(key);
                }
            }
        }
        globalLock.unlock();
    }

    /**
     * 保证一个key对应一个lock
     * key与lock是一对一的关系,lock与线程是一对多的关系
     * @param key
     * @return
     * @throws InterruptedException 
     */
    private RedisLockThread saveLock(String key) {
        globalLock.lock();//保证 saveLock 方法是原子操作
        //保证一个 key 只创建一个 lock
        RedisLockThread lockThread = keyLockMap.get(key);
        if(lockThread == null) {
            lockThread = new RedisLockThread(fair);
            lockThread.saveThreadOfLock(Thread.currentThread());//保存 lock 与 线程的关系
            keyLockMap.put(key, lockThread);//保存 key 与  lock 的关系
        }
        lockThread.saveThreadOfLock(Thread.currentThread());//保存 lock 与 线程的关系
        globalLock.unlock();
        return lockThread;
    }


    /**
     * 保证一个key对应一个lock
     * key与lock是一对一的关系,lock与线程是一对多的关系
     * @param key
     * @return
     * @throws InterruptedException 
     */
    private RedisLockThread saveLockInterruptibly(String key) throws InterruptedException{
        globalLock.lockInterruptibly();//保证 saveLockInterruptibly 方法是原子操作
        //保证一个 key 只创建一个 lock
        RedisLockThread lockThread = keyLockMap.get(key);
        if(lockThread == null) {
            lockThread = new RedisLockThread(fair);
            lockThread.saveThreadOfLock(Thread.currentThread());//保存 lock 与 线程的关系
            keyLockMap.put(key, lockThread);//保存 key 与  lock 的关系
        }
        lockThread.saveThreadOfLock(Thread.currentThread());//保存 lock 与 线程的关系
        globalLock.unlock();
        return lockThread;
    }

    private boolean isBlank(CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0)
            return true;
        for (int i = 0; i < strLen; i++)
            if (!Character.isWhitespace(cs.charAt(i)))
                return false;

        return true;
    }

    /**
     * 调试使用
     * @return
     */
    public String getContentOfKeyLockMap(String key){
        StringBuilder str = new StringBuilder();
        str.append("keyLock count:").append(keyLockMap.size());

        RedisLockThread lockThread = keyLockMap.get(key);
        if(lockThread != null) {
            str.append(",keyLockInfo:").append(lockThread.toString());
        } else {
            str.append(",keyLockInfo:").append("no lock");
        }

        return str.toString();
    }

    /**
     * redis的一个key,对应一个 RedisLockThread 实例对象
     */
    class RedisLockThread {

        private ReentrantLock lock;

        private volatile Map<Thread,Thread>  exclusiveOwnerThread = new HashMap<Thread,Thread>();

        public RedisLockThread(boolean fair) {
            this.lock = new ReentrantLock(fair);
        }

        protected ReentrantLock getLock() {
            return lock;
        }

        protected void saveThreadOfLock(Thread thread) {
            exclusiveOwnerThread.put(thread, thread);
        }

        protected Thread getThreadOfLock(Thread thread) {
            return exclusiveOwnerThread.get(thread);
        }

        protected void removeThreadOfLock(Thread thread) {
            exclusiveOwnerThread.remove(thread);
        }

        protected int getThreadsOfLock() {
            return exclusiveOwnerThread.size();
        }

        @Override
        public String toString() {
            StringBuffer str = new StringBuffer();
            str.append(" thread count of keyLock=").append(exclusiveOwnerThread.size());
            str.append(",keyLock=").append(lock.toString()).append(" ");
            return str.toString();
        }
    }
}

问与答

问:使用redis的什么指令实现的,分布式锁。
答:setnx

问:集群中有两台机器A、B,每台机器分别有10个线程需要获取redis lock。如果B机器的某个线程获取了redis lock,并且一直没有释放。这时 A 机器的10个线程会都挂起吗?
答:如果A机器的10个线程都挂起的话,那么将无法唤醒10个线程。所以 A 机器的10线程其中有9个会挂起,另外的一个线程会不断的尝试获取 redis lock,当获取blockingAfterLockNum参数配置次数后,仍然没有获取redis lock的时候,则挂起 parkThreadMilliscond 参数配置的时间。挂起结束后继续尝试获取 redis lock。

<think> 分布式锁是在分布式系统中用来协调多个节点对共享资源的访问的一种同步机制。在Java中实现分布式锁有多种方式,比如使用Redis、ZooKeeper、Etcd等。这里我将以Redis为例,展示如何使用Redisson客户端实现分布式锁。 ### 使用Redisson实现分布式锁 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid),提供了分布式锁的实现。 #### 步骤: 1. 添加Redisson依赖 2. 配置Redisson客户端 3. 使用Redisson的分布式锁API #### 示例代码: 首先,添加Maven依赖: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.17.0</version> <!-- 使用最新版本 --> </dependency> ``` 然后,编写代码: ```java import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; public class DistributedLockExample { public static void main(String[] args) { // 1. 创建Redisson配置 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); // 使用单节点模式 // 2. 创建Redisson客户端 RedissonClient redisson = Redisson.create(config); // 3. 获取锁对象 RLock lock = redisson.getLock("myLock"); try { // 4. 尝试加锁,最多等待100秒,加锁后30秒自动解锁 boolean isLocked = lock.tryLock(100, 30, java.util.concurrent.TimeUnit.SECONDS); if (isLocked) { // 5. 获取锁成功,执行临界区代码 System.out.println("Lock acquired, executing critical section"); Thread.sleep(10000); // 模拟业务操作 } } catch (InterruptedException e) { e.printStackTrace(); } finally { // 6. 释放锁 lock.unlock(); System.out.println("Lock released"); } // 7. 关闭Redisson客户端 redisson.shutdown(); } } ``` ### 解释: 1. **配置Redisson客户端**:这里使用单节点Redis服务器配置,地址为`redis://127.0.0.1:6379`。 2. **获取锁对象**:通过`redisson.getLock("myLock")`获取一个分布式锁对象,锁的键为`myLock`。 3. **尝试加锁**: - `lock.tryLock(100, 30, TimeUnit.SECONDS)`:尝试获取锁,最多等待100秒,如果获取到锁,锁的租期时间为30秒(即30秒后自动释放,防止死锁)。在业务执行时间超过租期时间时,锁会自动释放,因此需要确保业务逻辑在租期内完成,或者使用看门狗机制自动续期(Redisson默认支持看门狗,这里没有使用是因为我们指定了租期时间,如果不想指定租期,可以使用`lock.lock()`,此时看门狗会生效)。 4. **释放锁**:在finally块中释放锁,确保锁一定被释放。 5. **关闭客户端**:程序退出时关闭Redisson客户端。 ### 注意事项: - **死锁问题**:使用分布式锁时,必须确保锁最终被释放。因此,在finally块中释放锁是必要的。 - **锁续期**:如果业务操作时间可能超过锁的租期,则需要使用锁续期机制(Redisson的看门狗机制)。在示例中,我们指定了租期时间,如果业务操作时间超过30秒,锁会自动释放,可能导致临界区代码被多个线程同时执行。为了避免这种情况,可以: - 使用`lock.lock()`方法,该方法会启动看门狗,默认每隔30秒续期一次(默认锁超时时间30秒)。 - 或者确保业务操作在租期内完成。 - **网络分区**:在分布式系统中,网络分区可能导致锁失效(比如Redis主从切换时),可以考虑使用RedLock算法(Redisson提供了`RedissonRedLock`),但RedLock也存在争议。 ### 其他实现方式 除Redis外,还可以使用: - **ZooKeeper**:通过创建临时顺序节点实现分布式锁。 - **数据库**:使用数据库的唯一索引或乐观锁实现,但性能较低。 - **Etcd**:类似于ZooKeeper,通过租约(Lease)实现。 ### 总结 使用Redisson实现分布式锁相对简单且高效。但需要注意锁的超时设置和业务执行时间的关系,避免锁提前释放或者死锁。 §§相关问题§§: 1. 分布式锁需要满足哪些基本要求? 2. Redisson的看门狗机制是如何工作的? 3. 什么是RedLock算法?它解决了什么问题? 4. 在ZooKeeper中如何实现分布式锁? </think> ### 如何实现分布式锁(基于RedisRedisson实现) 分布式锁用于协调分布式系统中多个节点对共享资源的访问。以下是使用RedisRedisson库实现分布式锁的完整方案: #### 实现代码 ```java import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; public class DistributedLockExample { public static void main(String[] args) { // 1. 配置Redisson客户端 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); // 2. 创建Redisson实例 RedissonClient redisson = Redisson.create(config); // 3. 获取分布式锁对象 RLock lock = redisson.getLock("resourceLock"); try { // 4. 尝试获取锁(等待10秒,锁自动释放时间30秒) boolean isLocked = lock.tryLock(10, 30, java.util.concurrent.TimeUnit.SECONDS); if (isLocked) { System.out.println("成功获取分布式锁"); // 5. 执行受保护的临界区代码 accessSharedResource(); } else { System.out.println("获取锁失败,有其他节点持有锁"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("获取锁过程被中断"); } finally { // 6. 释放锁(只能在加锁线程中调用) if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); System.out.println("分布式锁已释放"); } // 7. 关闭Redisson客户端 redisson.shutdown(); } } private static void accessSharedResource() { // 模拟对共享资源的操作 System.out.println("正在访问共享资源..."); try { Thread.sleep(5000); // 模拟业务处理耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("共享资源访问完成"); } } ``` #### 实现解释 1. **Redisson配置**: - 使用单节点Redis服务器(可扩展为集群模式) - 连接地址为`redis://127.0.0.1:6379` 2. **核心机制**: - **锁获取**:`tryLock(waitTime, leaseTime, unit)` - `waitTime=10s`:最多等待10秒获取锁 - `leaseTime=30s`:获取后自动释放时间(防死锁) - **看门狗机制**(Redisson特有): - 后台线程每10秒检查锁状态 - 若业务未完成,自动续期锁时间(默认30秒) - **原子性保证**:Redis Lua脚本确保加锁操作的原子性 3. **关键特性**: - **互斥性**:同一时刻只有一个客户端能持有锁 - **防死锁**:自动过期释放 + 手动释放双保险 - **容错性**:Redis持久化保证锁状态不丢失 - **可重入性**:同一线程可多次获取同一把锁 4. **最佳实践**: - 总是使用`try-finally`确保锁释放 - 验证`lock.isHeldByCurrentThread()`避免误释放 - 根据业务合理设置过期时间(不宜过短/过长) #### Maven依赖 ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.23.1</version> </dependency> ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值