文章目录
- Redisson问题引入
- Java锁问题
- 问题重现
- 问题原因
- 如何解决
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);}@Testpublic 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();}}@Testpublic 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();}}
}