注意点:锁的名称要起好(如加上当前涉及表记录id),锁的粒度,越细越快:具体缓存的是某个数据,11号商品
一、整合redisson作为分布式锁等功能框架
1.pom引入依赖(sgg159)
<!-- 以后使用redisson作为所有分布式锁,分布式对象等功能框架-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.1</version>
</dependency>
2.配置类(sgg159)
可在配置文件中配置集群等,具体参考下面地址
https://github.com/redisson/redisson/wiki/2.-Configuration#25-replicated-mode
package com.product.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class MyRedissonConfig {
/**
* 所有对 Redisson 的使用都是通过 RedissonClient
*
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")//服务销毁会调用这个方法
public RedissonClient redisson() throws IOException {
// 1、创建配置
Config config = new Config();
// 注意redis的url格式 否则报错Redis url should start with redis:// or rediss://
//redis使用单节点,可使用redis集群
config.useSingleServer().setAddress("redis://192.168.163.131:6379");
//使用redis集群
//config.useClusterServers()
// .addNodeAddress("127,0.0.1:7004","127.0,.1:7001");
// 2、根据 Config 创建出 RedissonClient 实例
return Redisson.create(config);
}
}
3.redis 分布式锁使用 只能同时一个在进行写操作(sgg160和sgg161)
@Controller
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@ResponseBody
@GetMapping(value = "/hello")
public String hello() {
//1、获取一把锁,只要锁的名字一样,就是同一把锁
RLock myLock = redisson.getLock("my-lock");
//2、加锁
//myLock.lock(); //阻塞式等待。默认加的锁都是30s
//1)、锁的自动续期,如果业务超长,运行期间自动锁上新的30s。不用担心业务时间长,锁自动过期被删掉
//2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题
//3)、建议还是指定过期时间,理由是业务一般应该在30s之内执行完,同时可以省掉redis很多操作
myLock.lock(30,TimeUnit.SECONDS); //10秒钟自动解锁,自动解锁时间一定要大于业务执行时间
//问题:在锁时间到了以后,不会自动续期
//1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是 我们制定的时间
//2、如果我们没有指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】
// 只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动的再次续期,续成30秒
// internalLockLeaseTime 【看门狗时间】 / 3, 10s
try {
System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
} catch (Exception ex) {
ex.printStackTrace();
} finally {
//3、解锁 假设解锁代码没有运行,Redisson不会出现死锁问题
System.out.println("释放锁..." + Thread.currentThread().getId());
myLock.unlock();
}
return "hello";
}
}
4.redis读写锁 (sgg162和sgg163)
@Controller
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁
* 写锁没释放读锁必须等待
*
* 注意点:
* (1)读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
* (2)写 + 读 :必须等待写锁释放
* (3)写 + 写 :阻塞方式
* (4)读 + 写 :有读锁。写也需要等待
* 只要有读或者写的存都必须等待
* @return
*/
@GetMapping(value = "/write")
@ResponseBody
public String writeValue() {
String s = "";
//1.获取读写锁
RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
RLock rLock = readWriteLock.writeLock();
try {
//2、改数据加写锁,读数据加读锁
rLock.lock();
//3.业务处理
s = UUID.randomUUID().toString();
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set("writeValue",s);
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//4.释放锁
rLock.unlock();
}
return s;
}
@GetMapping(value = "/read")
@ResponseBody
public String readValue() {
String s = "";
//1.获取读写锁
RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
RLock rLock = readWriteLock.readLock();
try {
//2.加读锁
rLock.lock();
//3.业务处理
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
s = ops.get("writeValue");
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.释放锁
rLock.unlock();
}
return s;
}
}
5.redis闭锁(等其它几个结束再执行自己的) (sgg164)
@Controller
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 放假、锁门
* 1班没人了
* 5个班,全部走完,我们才可以锁大门
* 分布式闭锁
*/
@GetMapping(value = "/lockDoor")
@ResponseBody
public String lockDoor() throws InterruptedException {
RCountDownLatch door = redisson.getCountDownLatch("door");
door.trySetCount(5);
door.await(); //等待闭锁完成
return "放假了...";
}
@GetMapping(value = "/gogogo/{id}")
@ResponseBody
public String gogogo(@PathVariable("id") Long id) {
RCountDownLatch door = redisson.getCountDownLatch("door");
door.countDown(); //计数-1
return id + "班的人都走了...";
}
}
6.redis信号量(等有位置才执行自己的) (sgg165)
@Controller
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 车库停车
* 3车位
* 信号量也可以做分布式限流
*/
@GetMapping(value = "/park")
@ResponseBody
public String park() throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
//park.acquire();阻塞,有位才会继续执行 //获取一个信号、获取一个值,占一个车位
boolean flag = park.tryAcquire();//非阻塞
if (flag) {
//执行业务
} else {
return "error";
}
return "ok=>" + flag;
}
@GetMapping(value = "/go")
@ResponseBody
public String go() {
RSemaphore park = redisson.getSemaphore("park");
park.release(); //释放一个车位
return "ok";
}
}
7.注意点:锁的粒度(sgg166)
/**
* 缓存里的数据如何和数据库的数据保持一致??
* 缓存数据一致性
* 1)、双写模式
* 2)、失效模式
*
* @return
*/
public Map<String, List<Catalogs2Vo>> getCatalogJsonFromDbWithRedissonLock() {
//1、占分布式锁。去redis占坑
//(锁的粒度,越细越快:具体缓存的是某个数据,11号商品) product-11-lock
//RLock catalogJsonLock = redissonClient.getLock("catalogJson-lock");
//创建读锁
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");
RLock rLock = readWriteLock.readLock();
Map<String, List<Catalogs2Vo>> dataFromDb = null;
try {
//2.加锁成功
rLock.lock();
//3.执行业务
dataFromDb = getCatalogJsonFromDB();
} finally {
//4.释放锁
rLock.unlock();
}
return dataFromDb;
}