package com.alatus.mall.product.web; import com.alatus.mall.product.entity.CategoryEntity; import com.alatus.mall.product.service.CategoryService; import com.alatus.mall.product.vo.SubCatalogVo; import org.redisson.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @Controller public class IndexController { @Autowired private CategoryService categoryService; @Autowired private RedissonClient redisson; @Autowired private StringRedisTemplate redisTemplate; @GetMapping({"/","/index.html"}) public String indexPage(Model model){ List<CategoryEntity> categoryEntities = categoryService.getLevelCategories(1); // 视图解析器会自动拼串,不需要写具体位置和html后缀 model.addAttribute("categories",categoryEntities); return "index"; } @GetMapping("/index/catalog.json") @ResponseBody public Map<String, List<SubCatalogVo>> getCatalogJson(){ return categoryService.getCatalogJson(); } @GetMapping("/hello") @ResponseBody public String hello() throws InterruptedException { // 获取一把锁,只要名字一样,就必定是同一把锁,底层用的原理还是redis RLock rLock = redisson.getLock("rLock"); rLock.lock(); // 加锁 // 加的锁会自带阻塞式等待,默认锁的时间是30s // 锁会自动续期,如果业务超长,会自动给锁续上30s,不用像我们手动加锁一样,担心锁被自动删除 // 只要业务完成,就不会续期,即使不手动解锁,也会在30s后删除 // 只要我们没配置过期时间,占锁成功就会启动一个定时任务,会自动给锁设置过期时间,新的过期时间就是看门狗的默认时间 // 相当于每隔10s都会自动续期 // rLock.lock(10, TimeUnit.SECONDS); // 相对来说,更推荐的是指定时间的方式,因为这样可以减少定时任务带来的损耗 // 自己设置了锁的持续时间以后,会导致超时自动续时间失败 // 其他服务就能抢到锁了 // 因此,我们设置的自动解锁时间一定是要大于我们的业务执行时间的 // 如果我们传递了锁的超时时间,就会发给redis执行lua脚本,底层还是lua脚本的形式 // 默认时间就被修改为我们传递的时间了 // 默认时间是底层配置的看门狗cfg时间30*1000 // 只要占锁成功,就会启动一个定时任务,重新给锁设置新的过期时间 // 默认的续期触发是看门狗时间除以三 // 尝试加锁,最多等待100s,上锁后10s自动解除 boolean lock = rLock.tryLock(100, 10, TimeUnit.SECONDS); // 公平锁,他会优先分配给先发出请求的线程,所有线程会排队 // 所有请求线程会在队列中排队,当某个线程宕机了,等待5s后会继续下一个线程,如果前面5个线程全都等待,后面的线程会等待25s RLock fairLock = redisson.getFairLock("lock"); // 读写锁 RReadWriteLock readWriteLock = redisson.getReadWriteLock("qLock"); // 业务读取数据用读锁 readWriteLock.readLock().lock(); // 业务写入数据用写锁 readWriteLock.writeLock().lock(); // 写锁存在,读锁等待,读锁存在,写锁等待 try{ System.out.println("加锁成功执行业务"); Thread.sleep(30000); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { // 解锁,假设业务代码出现问题或是没有执行,redisson也不会出现死锁 rLock.unlock(); // 如果其他服务抢到了这个锁,执行删除,逻辑判断就会发现锁不对 // 会导致我们的删锁操作失败并报错 } return "Hello"; } // 使用读写锁能保证我们的业务始终能读到最新数据 // 写锁是一个排他锁,互斥锁,读锁是一个共享锁 // 写锁存在,读锁就必须等待 @GetMapping("/write") @ResponseBody public String writeValue(){ String string = UUID.randomUUID().toString(); RReadWriteLock myLock = redisson.getReadWriteLock("myLock"); RLock rLock = myLock.writeLock(); // 修改数据加写入锁 rLock.lock(); try { redisTemplate.opsForValue().set("uuid",string); Thread.sleep(30000); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { rLock.unlock(); } return string; } // 读锁没释放的时候,写锁也是无法占锁的 // 不会导致读取的业务进行中,但原始数据被修改的情况 // 只要有写入锁的存在,无论是先写还是后写,都必须等待释放才能继续 @GetMapping("/read") @ResponseBody public String readValue(){ RReadWriteLock readWriteLock = redisson.getReadWriteLock("myLock"); RLock rLock = readWriteLock.readLock(); rLock.lock(); String string = ""; // 读取数据加读取锁 try { string = redisTemplate.opsForValue().get("uuid"); Thread.sleep(30000); } catch (Exception e){ e.printStackTrace(); } finally { rLock.unlock(); } return string; } // 闭锁,要多个线程全部完成了自己的任务才算彻底结束 @GetMapping("/close") @ResponseBody public String closeDoor() throws InterruptedException { RCountDownLatch door = redisson.getCountDownLatch("door"); door.trySetCount(5); // 等待所有闭锁完成 door.await(); return "放假了"; } @GetMapping("/go/{id}") @ResponseBody public String goGo(@PathVariable("id")Long id){ RCountDownLatch countDownLatch = redisson.getCountDownLatch("door"); // 计数减少1 countDownLatch.countDown(); // JUC下也有闭锁 return id.toString(); } // 信号量,可过期信号量都在此前演示过了 }
package com.alatus.mall.product.web; import com.alatus.mall.product.entity.CategoryEntity; import com.alatus.mall.product.service.CategoryService; import com.alatus.mall.product.vo.SubCatalogVo; import org.redisson.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @Controller public class IndexController { @Autowired private CategoryService categoryService; @Autowired private RedissonClient redisson; @Autowired private StringRedisTemplate redisTemplate; @GetMapping({"/","/index.html"}) public String indexPage(Model model){ List<CategoryEntity> categoryEntities = categoryService.getLevelCategories(1); // 视图解析器会自动拼串,不需要写具体位置和html后缀 model.addAttribute("categories",categoryEntities); return "index"; } @GetMapping("/index/catalog.json") @ResponseBody public Map<String, List<SubCatalogVo>> getCatalogJson(){ return categoryService.getCatalogJson(); } @GetMapping("/hello") @ResponseBody public String hello() throws InterruptedException { // 获取一把锁,只要名字一样,就必定是同一把锁,底层用的原理还是redis RLock rLock = redisson.getLock("rLock"); rLock.lock(); // 加锁 // 加的锁会自带阻塞式等待,默认锁的时间是30s // 锁会自动续期,如果业务超长,会自动给锁续上30s,不用像我们手动加锁一样,担心锁被自动删除 // 只要业务完成,就不会续期,即使不手动解锁,也会在30s后删除 // 只要我们没配置过期时间,占锁成功就会启动一个定时任务,会自动给锁设置过期时间,新的过期时间就是看门狗的默认时间 // 相当于每隔10s都会自动续期 // rLock.lock(10, TimeUnit.SECONDS); // 相对来说,更推荐的是指定时间的方式,因为这样可以减少定时任务带来的损耗 // 自己设置了锁的持续时间以后,会导致超时自动续时间失败 // 其他服务就能抢到锁了 // 因此,我们设置的自动解锁时间一定是要大于我们的业务执行时间的 // 如果我们传递了锁的超时时间,就会发给redis执行lua脚本,底层还是lua脚本的形式 // 默认时间就被修改为我们传递的时间了 // 默认时间是底层配置的看门狗cfg时间30*1000 // 只要占锁成功,就会启动一个定时任务,重新给锁设置新的过期时间 // 默认的续期触发是看门狗时间除以三 // 尝试加锁,最多等待100s,上锁后10s自动解除 boolean lock = rLock.tryLock(100, 10, TimeUnit.SECONDS); // 公平锁,他会优先分配给先发出请求的线程,所有线程会排队 // 所有请求线程会在队列中排队,当某个线程宕机了,等待5s后会继续下一个线程,如果前面5个线程全都等待,后面的线程会等待25s RLock fairLock = redisson.getFairLock("lock"); // 读写锁 RReadWriteLock readWriteLock = redisson.getReadWriteLock("qLock"); // 业务读取数据用读锁 readWriteLock.readLock().lock(); // 业务写入数据用写锁 readWriteLock.writeLock().lock(); // 写锁存在,读锁等待,读锁存在,写锁等待 try{ System.out.println("加锁成功执行业务"); Thread.sleep(30000); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { // 解锁,假设业务代码出现问题或是没有执行,redisson也不会出现死锁 rLock.unlock(); // 如果其他服务抢到了这个锁,执行删除,逻辑判断就会发现锁不对 // 会导致我们的删锁操作失败并报错 } return "Hello"; } // 使用读写锁能保证我们的业务始终能读到最新数据 // 写锁是一个排他锁,互斥锁,读锁是一个共享锁 // 写锁存在,读锁就必须等待 @GetMapping("/write") @ResponseBody public String writeValue(){ String string = UUID.randomUUID().toString(); RReadWriteLock myLock = redisson.getReadWriteLock("myLock"); RLock rLock = myLock.writeLock(); // 修改数据加写入锁 rLock.lock(); try { redisTemplate.opsForValue().set("uuid",string); Thread.sleep(30000); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { rLock.unlock(); } return string; } // 读锁没释放的时候,写锁也是无法占锁的 // 不会导致读取的业务进行中,但原始数据被修改的情况 // 只要有写入锁的存在,无论是先写还是后写,都必须等待释放才能继续 @GetMapping("/read") @ResponseBody public String readValue(){ RReadWriteLock readWriteLock = redisson.getReadWriteLock("myLock"); RLock rLock = readWriteLock.readLock(); rLock.lock(); String string = ""; // 读取数据加读取锁 try { string = redisTemplate.opsForValue().get("uuid"); Thread.sleep(30000); } catch (Exception e){ e.printStackTrace(); } finally { rLock.unlock(); } return string; } // 闭锁,要多个线程全部完成了自己的任务才算彻底结束 @GetMapping("/close") @ResponseBody public String closeDoor() throws InterruptedException { RCountDownLatch door = redisson.getCountDownLatch("door"); door.trySetCount(5); // 等待所有闭锁完成 door.await(); return "放假了"; } @GetMapping("/go/{id}") @ResponseBody public String goGo(@PathVariable("id")Long id){ RCountDownLatch countDownLatch = redisson.getCountDownLatch("door"); // 计数减少1 countDownLatch.countDown(); // JUC下也有闭锁 return id.toString(); } // 信号量,可过期信号量都在此前演示过了 }
Redis使用redisson分布式并发锁闭锁案例演示和实操-----Redis
最新推荐文章于 2024-08-25 21:09:34 发布