保证同一时间只有⼀个客户端可以对共享资源进⾏操作
为了防⽌分布式系统中的多个进程之间相互⼲扰,需要⼀种分布式协调技术来对这些进程进⾏调度利用互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题

设计分布式锁应该考虑的东西

  • 排他性
    • 在分布式应⽤集群中,同⼀个⽅法在同⼀时间只能被⼀台机器上的⼀个线程执行
  • 容错性
    • 分布式锁⼀定能得到释放,⽐如客户端奔溃或者网络中断
  • 满⾜可重⼊、⾼性能、⾼可⽤
  • 注意分布式锁的开销、锁粒度

基于Redis实现

加锁并配置过期时间,保证原子性;解锁要防止误删除,并保证原子性

lua脚本有一定的原子性,不会背其它命令插队,可以完成一些redis事务性的操作

  1. @RestController
  2. @RequestMapping("/lock")
  3. public class TestLock {
  4. @Resource
  5. private StringRedisTemplate stringRedisTemplate;
  6. @GetMapping("/test")
  7. public void distributeLock(@RequestParam("pid")Long pid) {
  8. String key = "distribute:" + pid;
  9. //uuid防止其它线程误删
  10. String uuid = UUID.randomUUID().toString().substring(1, 7);
  11. lock(key, uuid);
  12. }
  13. private void lock(String key, String uuid) {
  14. //线程加锁
  15. Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, 10, TimeUnit.MINUTES);
  16. System.out.println("线程"+uuid+"加锁状态"+isLock);
  17. if (isLock) {
  18. try {
  19. //todo 业务逻辑
  20. System.out.println("线程"+uuid+"处理业务");
  21. TimeUnit.SECONDS.sleep(5);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. } finally {
  25. //这里删除key要保证原子性 用lua脚本执行
  26. stringRedisTemplate.delete(key);
  27. System.out.println("线程"+uuid+"解锁");
  28. }
  29. } else {
  30. //自旋争取
  31. try {
  32. System.out.println("线程"+uuid+"开始自旋");
  33. TimeUnit.SECONDS.sleep(2);
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. }
  37. lock(key, uuid);
  38. }
  39. }
  40. }