文章目录
前言
Redisson是Redis官方推荐的Java版的Redis客户端。它提供的功能非常多,也非常强大,本文我们仅关注分布式锁的实现,更多请参考,官方文档
一、环境搭建
导入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
开启配置
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.170.130:6379");
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
二、可重入锁
默认,非公平锁
- 锁的自动续期:如果业务运行时间超长,运行期间会自动给锁上新的30s,不用担心业务实际长,锁自动被删掉。
- 加锁的业务只要完成,就不会给当前锁续期,即使不手动解锁,也会在30s后自动删除锁。
public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisson() {
Map<String, List<Catalog2Vo>> categoryMap=null;
//获得锁,名字为CatalogJson-Lock
RLock lock = redissonClient.getLock("CatalogJson-Lock");
//上锁,阻塞式等待,默认加的锁都是30s
lock.lock();
try {
Thread.sleep(30000);
//业务逻辑
categoryMap = getCategoryMap();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//解锁
lock.unlock();
return categoryMap;
}
}
lock.lock() 和 lock.lock(10, TimeUnit.SECONDS)区别
- 如果我们指定了超时时间,就会发送给redis执行脚本,超过这个时间后锁便自动解开了。不会自动续期,无需调用unlock方法手动解锁,如果要手动解锁一定要确保业务执行时间小于锁的失效时间
- 如果没有指定超时时间,就会使用30*1000(看门口的默认时间30s也可以通过修改Config.lockWatchdogTimeout来另行指定)。超过了1/3的看门狗时间(默认为10s,也就是还剩20s时就会重新续成30s),就会自动续期。
tryLock
有最大等待时间,抢不到锁,不会一直傻傻的等待。
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
读写锁(ReadWriteLock)
写锁会阻塞读锁,但是读锁不会阻塞读锁,但读锁会阻塞写锁
总之含有写的过程都会被阻塞,只有读读不会被阻塞
上锁时在redis的状态
@GetMapping("/read")
@ResponseBody
public String read() {
RReadWriteLock lock = redissonClient.getReadWriteLock("ReadWrite-Lock");
RLock rLock = lock.readLock();
String s = "";
try {
rLock.lock();
System.out.println("读锁加锁"+Thread.currentThread().getId());
Thread.sleep(5000);
s= redisTemplate.opsForValue().get("lock-value");
}finally {
rLock.unlock();
return "读取完成:"+s;
}
}
@GetMapping("/write")
@ResponseBody
public String write() {
RReadWriteLock lock = redissonClient.getReadWriteLock("ReadWrite-Lock");
RLock wLock = lock.writeLock();
String s = UUID.randomUUID().toString();
try {
wLock.lock();
System.out.println("写锁加锁"+Thread.currentThread().getId());
Thread.sleep(10000);
redisTemplate.opsForValue().set("lock-value",s);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
wLock.unlock();
return "写入完成:"+s;
}
}
信号量(Semaphore)
信号量为存储在redis中的一个数字,当这个数字大于0时,即可以调用acquire()
方法增加数量,也可以调用release()
方法减少数量,但是当调用release()
之后小于0的话方法就会阻塞,直到数字大于0
@GetMapping("/park")
@ResponseBody
public String park() {
RSemaphore park = redissonClient.getSemaphore("park");
try {
park.acquire(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "停进2";
}
@GetMapping("/go")
@ResponseBody
public String go() {
RSemaphore park = redissonClient.getSemaphore("park");
park.release(2);
return "开走2";
}
闭锁(CountDownLatch)
可以理解为门栓,使用若干个门栓将当前方法阻塞,只有当全部门栓都被放开时,当前方法才能继续执行。
以下代码只有offLatch()
被调用5次后 setLatch()
才能继续执行
@GetMapping("/setLatch")
@ResponseBody
public String setLatch() {
RCountDownLatch latch = redissonClient.getCountDownLatch("CountDownLatch");
try {
latch.trySetCount(5);
latch.await();//等待闭锁都完成
//业务代码...
} catch (InterruptedException e) {
e.printStackTrace();
}
return "门栓被放开";
}
@GetMapping("/offLatch")
@ResponseBody
public String offLatch() {
RCountDownLatch latch = redissonClient.getCountDownLatch("CountDownLatch");
latch.countDown();
return "门栓被放开1";
}
闭锁在redis的存储状态