文章目录
一. 读写锁RReadWriteLock写写锁、写读锁使用不互斥阻塞问题
在测试redisson的读写锁RReadWriteLock时,发现写写锁、写读锁不互斥阻塞。同样的线程代码在测试JUC下的读写锁ReentrantReadWriteLock,则不存在写写锁、写读锁不互斥阻塞问题。
一度怀疑redisson版本问题,换了几个版本存在同样的问题。 后来发现程序执行时间超过锁定看门狗超时时间(config.setLockWatchdogTimeout),会引起写写锁、写读锁不互斥阻塞. 这个问题找了N久!!!!!!
二. lockWatchdogTimeout 参数作用
如果获取锁的 Redisson 实例崩溃,则此类锁可以永远处于获取状态。为了避免这种情况 Redisson 维护锁看门狗,它会在锁持有人 Redisson 实例处于活动状态时延长锁过期时间。默认情况下,锁定看门狗超时为 30 秒,可以通过 Config.lockWatchdogTimeout 设置进行更改。
三. 测试代码
RedissonUtil
public class RedissonUtil {
public static RedissonClient redissonClient;
static {
Config config = new Config();
config.setLockWatchdogTimeout(60) //锁定看门狗超时设置
.useSingleServer()
.setAddress("redis://192.168.64.130:6379")
.setDatabase(1);
redissonClient = Redisson.create(config);
}
public RedissonClient getRedissonClient() {
return redissonClient;
}
}
BaseTest
public class BaseTest {
protected RedissonClient redissonClient;
@Before
public void init(){
RedissonUtil redissonUtil = new RedissonUtil();
redissonClient = redissonUtil.getRedissonClient();
}
protected void sleep(long time){
try {
Thread.sleep(time);
System.out.println(Thread.currentThread().getName()+" 线程等待,"+ time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
TestReentrantReadWriteLock
public class TestReentrantReadWriteLock extends BaseTest {
static String us ="00000";
@Test
public void testReadWriteLock(){
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);
Thread thread1 = new Thread(()->runWriteLock(readWriteLock));
Thread thread2 = new Thread(()->runReadLock(readWriteLock));
Thread thread3 = new Thread(()->runReadLock(readWriteLock));
Thread thread4 = new Thread(()->runWriteLock(readWriteLock));
Thread thread5 = new Thread(()->runReadLock(readWriteLock));
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
while (true){
}
}
private void runReadLock(ReentrantReadWriteLock readWriteLock) {
long start = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+" 开始尝试取读锁,"+start);
ReentrantReadWriteLock.ReadLock rLock = readWriteLock.readLock();
try {
boolean res = rLock.tryLock(10L, TimeUnit.SECONDS);
if(res){
//成功获得锁,在这里处理业务
System.out.println(Thread.currentThread().getName()+" 获取读锁成功,"+(System.currentTimeMillis() - start)+","+us);
sleep(5000);
}
} catch (Exception e) {
System.out.println("获取读锁失败,失败原因:" + e.getMessage());
} finally {
//无论如何, 最后都要解锁
rLock.unlock();
System.out.println(Thread.currentThread().getName()+" 解读锁成功,"+(System.currentTimeMillis() - start)+"\n");
}
}
private void runWriteLock(ReentrantReadWriteLock readWriteLock) {
long start = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+" 开始尝试取-写锁,"+start);
ReentrantReadWriteLock.WriteLock rLock = readWriteLock.writeLock();
try {
boolean res = rLock.tryLock(10L, TimeUnit.SECONDS);
if(res){
//成功获得锁,在这里处理业务
System.out.println(Thread.currentThread().getName()+" 获取--写锁--成功,"+(System.currentTimeMillis() - start)+","+us);
us = Thread.currentThread().getName();
//等待,占用锁5S
sleep(5000);
}
} catch (Exception e) {
System.out.println("获取写锁失败,失败原因:" + e.getMessage());
} finally {
//无论如何, 最后都要解锁
rLock.unlock();
System.out.println(Thread.currentThread().getName()+" 解--写锁--成功,"+(System.currentTimeMillis() - start)+"\n");
}
}
}
四. 测试结果
当程序执行时间
超过锁定看门狗超时时间,会引起写写、写读不互斥阻塞
config.setLockWatchdogTimeout(60) 写写、写读不会互斥,如setLockWatchdogTimeout时间为60s,但示例中程序超过了60,先获取的写锁提前失效,后面线程可以继续获取
当程序执行时间
未超过锁定看门狗超时时间,写写、写读会互斥阻塞
config.setLockWatchdogTimeout(600)
写锁+写锁 测试
- 执行时间
超过锁定看门狗超时时间,不互斥阻塞
Thread-1 开始尝试取-写锁,1688541911818
Thread-4 开始尝试取-写锁,1688541911818
Thread-1 获取--写锁--成功,21,00000
Thread-4 获取--写锁--成功,82,Thread-1
Thread-1 线程等待,5000
Thread-1 解--写锁--成功,5034 //写线程1 执行5s
Thread-4 线程等待,5000
Thread-4 解--写锁--成功,5094 //写线程4 执行5s,说明未阻塞
- 执行时间
未超过锁定看门狗超时时间,互斥阻塞
Thread-1 开始尝试取-写锁,1688541587287
Thread-4 开始尝试取-写锁,1688541587287
Thread-1 获取--写锁--成功,16,00000
Thread-1 线程等待,5000
Thread-1 解--写锁--成功,5026 //写线程1 执行5s
Thread-4 获取--写锁--成功,5032,Thread-1
Thread-4 线程等待,5000
Thread-4 解--写锁--成功,10038 //写线程4 执行10s,说明被阻塞
写锁+读锁 测试
- 执行时间
超过锁定看门狗超时时间,不互斥阻塞
Thread-1 开始尝试取-写锁,1688541770214
Thread-2 开始尝试取读锁,1688541770214
Thread-1 获取--写锁--成功,20,00000
Thread-2 线程等待,1000
Thread-2 获取读锁成功,1105,Thread-1
Thread-1 线程等待,5000
Thread-1 解--写锁--成功,5027 //写线程1 执行5s
Thread-2 线程等待,5000
Thread-2 解读锁成功,6119 //读线程2 执行6s,说明未阻塞
- 执行时间
未超过锁定看门狗超时时间,互斥阻塞
Thread-1 开始尝试取-写锁,1688542009667
Thread-2 开始尝试取读锁,1688542009667
Thread-1 获取--写锁--成功,20,00000
Thread-1 线程等待,5000
Thread-1 解--写锁--成功,5037 //写线程1 执行5s
Thread-2 线程等待,1000
Thread-2 获取读锁成功,6052,Thread-1
Thread-2 线程等待,5000
Thread-2 解读锁成功,11067 //读线程2 执行11s,说明阻塞
读锁+读锁 测试
- 执行时间
超过锁定看门狗超时时间,不互斥阻塞
Thread-2 开始尝试取读锁,1688542266988
Thread-3 开始尝试取读锁,1688542266989
Thread-2 线程等待,1000
Thread-2 获取读锁成功,1031,00000
Thread-3 线程等待,1000
Thread-3 获取读锁成功,1030,00000
Thread-3 线程等待,5000
Thread-2 线程等待,5000
Thread-3 解读锁成功,6048 //读线程3 执行6s
Thread-2 解读锁成功,6049 //读线程3 执行6s,说明未阻塞
- 执行时间
未超过锁定看门狗超时时间,不互斥阻塞
Thread-2 开始尝试取读锁,1688542186099
Thread-3 开始尝试取读锁,1688542186099
Thread-3 线程等待,1000
Thread-2 线程等待,1000
Thread-2 获取读锁成功,1035,00000
Thread-3 获取读锁成功,1035,00000
Thread-2 线程等待,5000
Thread-3 线程等待,5000
Thread-2 解读锁成功,6044 //读线程3 执行6s
Thread-3 解读锁成功,6044 //读线程3 执行6s,说明未阻塞

本文探讨了Redisson的读写锁RReadWriteLock在写写锁和写读锁使用时出现的非互斥阻塞问题,发现原因是lockWatchdogTimeout设置。当程序执行超过此超时时间,会导致锁不互斥。测试结果显示,锁的互斥行为取决于lockWatchdogTimeout的配置。
1634

被折叠的 条评论
为什么被折叠?



