案例:多线程同时进入查询,缓冲查询无意义
@GetMapping("/details/{id}")
@ResponseBody
public Student getOneDetails(@PathVariable Integer id){
for (int i = 0; i < 50; i++) {
ExecutorService executorService= Executors.newFixedThreadPool(2);
executorService.submit(new Runnable() {
@Override
public void run() {
selectOne(id);
}
});
}
return selectOne(id);
}
public Student selectOne(Integer id) {
//redis序列化的方式
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
//首先从缓存中获取
Student student= (Student) redisTemplate.opsForValue().get("studentSave");
if(null!=student){
log.info("redis方式");
return student;
}
//缓冲中无数据,从数据库中获取,并放入redis保存
log.info("mysql 方式");
student= studentDao.selectOne(id);
//磁盘的存储要求类实现序列化(Student implements Serializable)
redisTemplate.opsForValue().set("studentSave",student);
return student;
}
1:第一种解决方案:synchronized(单体运用下有效)
说明:synchronized 是 Java 中的一个关键字,synchronized可用来实现锁,锁的添与释放都有 jvm 控制,jvm 释放锁有两种情况
一种是线程执行完任务, jvm 释放锁,一种是 jvm 运行出现错误,释放锁资源,否则其他线程将一直等待
使用案例:
@GetMapping("/details/{id}")
@ResponseBody
public Student getOneDetails(@PathVariable Integer id){
for (int i = 0; i < 50; i++) {
ExecutorService executorService= Executors.newFixedThreadPool(2);
executorService.submit(new Runnable() {
@Override
public void run() {
selectOne(id);
}
});
}
return selectOne(id);
}
public Student selectOne(Integer id) {
//redis序列化的方式
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
//首先从缓存中获取
Student student= (Student) redisTemplate.opsForValue().get("studentSave");
if(null!=student){
log.info("redis1方式");
return student;
}
//使用synchronized使多线程实现同步查询
synchronized (this){
//再次判断缓存是否存在数据
student= (Student) redisTemplate.opsForValue().get("studentSave");
if(null!=student){
log.info("redis2方式");
return student;
}
//缓冲中无数据,从数据库中获取,并放入redis保存
log.info("mysql1 方式");
student= studentDao.selectOne(id);
//磁盘的存储要求类实现序列化(Student implements Serializable)
redisTemplate.opsForValue().set("studentSave",student);
}
return student;
}
2:第二种解决方案:lock(单体运用下有效)
lock是一个接口,Lock 的加锁和释放都需要手动操作,我们可以获取到目标线程是否获得锁,当大量线程同时竞争的时候,Lock 的性能会远高于synchronized
使用案例:
//定义锁
static Lock lock=new ReentrantLock();
@GetMapping("/details/{id}")
@ResponseBody
public Student getOneDetails(@PathVariable Integer id){
new Thread(new Runnable() {
@Override
public void run() {
selectOne(id);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
selectOne(id);
}
}).start();
return selectOne(id);
}
public Student selectOne(Integer id) {
//redis序列化的方式
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
//首先从缓存中获取
Student student= (Student) redisTemplate.opsForValue().get("studentSave");
if(null!=student){
log.info("redis1方式");
return student;
}
lock.lock();
try {
//需要同步的代码片段
if(lock.tryLock()){
//再次判断缓存是否存在数据
student= (Student) redisTemplate.opsForValue().get("studentSave");
if(null!=student){
log.info("redis2方式");
return student;
}
//缓冲中无数据,从数据库中获取,并放入redis保存
log.info("mysql1方式");
student= studentDao.selectOne(id);
//磁盘的存储要求类实现序列化(Student implements Serializable)
redisTemplate.opsForValue().set("studentSave",student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放锁资源
lock.unlock();
}
return student;
}