Java锁与Redisson锁的坑

本文分析了在使用Redisson分布式锁时引入的Java锁问题,尤其是在finally块中的unlock操作可能导致异常。通过实例展示了问题的重现、原因以及如何通过在tryLock成功后解锁来避免IllegalMonitorStateException。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redisson问题引入

系统中我们经常需要使用分布式锁,一个不错的选择是使用redisson。

那么,像下面这样使用redisson锁有问题吗?

如果有问题,是什么问题呢?

import org.junit.jupiter.api.Test;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class RedissonLockTest {

    private static final Config config = new Config();

    private static final RedissonClient redissonClient;

    static {
        config.useSingleServer()
                .setAddress("redis://127.0.0.1:6379");
        redissonClient = Redisson.create(config);
    }

    @Test
    public void thread() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                try {
                   tryLockBad();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        executorService.shutdown();
        while (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
            System.out.println("等待线程池任务执行完成...");
        }
        System.out.println("线程池任务执行完成");
    }

    public void tryLockBad() throws InterruptedException {
        RLock lock = redissonClient.getLock("try-lock-unlock");
        try {
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                System.out.println("获取锁当前线程id:" + Thread.currentThread());
            } else {
                System.out.println("没有获取到锁");
            }
        } finally {
            lock.unlock();
        }
    }
}

我们先看一下Java的锁,再来继续redisson这个问题。

Java锁问题

同样2个问题,下面的方式使用锁是否有问题?

如果有问题,是什么问题?

public class RedissonLockTest {
    private final ReentrantLock reentrantLock = new ReentrantLock();

    public void tryJavaLockBad() throws InterruptedException {
        try {
            if (reentrantLock.tryLock(2, TimeUnit.SECONDS)) {
                System.out.println("获取锁当前线程id:" + Thread.currentThread());
            } else {
                System.out.println("没有获取到锁");
            }
        } finally {
            reentrantLock.unlock();
        }
    }

    @Test
    public void thread() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                try {
                    tryJavaLockBad();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }
        executorService.shutdown();
        while (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
            System.out.println("等待线程池任务执行完成...");
        }
        System.out.println("线程池任务执行完成");
    }
}

看出来了吗?

问题重现

习惯了,只检查是否在finally中,看起来没啥问题,运行一下似乎也没有问题。

我们只需要做一点点改动就能在运行中看出问题,就是获取锁之后,让它多运行一会儿。

public void tryJavaLockBad() throws InterruptedException {
    try {
        if (reentrantLock.tryLock(2, TimeUnit.SECONDS)) {
            System.out.println("获取锁当前线程id:" + Thread.currentThread());
            TimeUnit.SECONDS.sleep(20);
        } else {
            System.out.println("没有获取到锁");
        }
    } finally {
        reentrantLock.unlock();
    }
}

然后,我们就能看到java.lang.IllegalMonitorStateException异常。

现在,看出问题了吗?

问题原因

那是因为finally的代码总会被执行,不管tryLock是否获取到锁。

当线程没有获取到锁,去执行unlock时,就会出现IllegalMonitorStateException异常。

redisson也一样。

如何解决

知道问题原因,解决就容易了。

只需要判断一下是否获取锁,获取到锁的时候才去unlock就可以。

public void tryLock() throws InterruptedException {
    RLock lock = redissonClient.getLock("try-lock-unlock");
    boolean getLock = false;
    try {
        getLock = lock.tryLock(2, TimeUnit.SECONDS);
        if (getLock) {
            System.out.println("获取锁当前线程id:" + Thread.currentThread());
            TimeUnit.SECONDS.sleep(10);
        } else {
            System.out.println("没有获取到锁");
        }
    } finally {
        if (getLock) {
            System.out.println("解锁当前线程id:" + Thread.currentThread());
            lock.unlock();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值