基于redis实现分布式锁,主要原理在于key,每次访问时判断当前key是否存在于redis中,若存在则阻塞,若不存在则加入redis中同时获取redis锁。
但是java中获取key值,及向redis中塞入key以下是两个操作不是原子性的
1、 redis.get(key)
2、redis.setEx(key,60*5)
当线程A和线程B发生并发时,A和B同时get(key),为null,则代表A和B都可以获取当前锁,从而发生并发问题。
所以此处使用redis事务来避免此问题发生。
1 public static boolean lock(String key){ 2 //获取redisTemplate 3 RedisTemplate redisTemplate= RedisHelper.getTemplate(); 4 Boolean rs = (Boolean) redisTemplate.execute(new RedisCallback(key, 60*5)); 5 return rs; 6 } 7 8 public static class RedisCallback implements org.springframework.data.redis.core.RedisCallback<Boolean> { 9 private byte[] key; 10 private int expire; 11 12 public RedisCallback(String key,int expire) { 13 this.expire = expire; 14 this.key = RedisHelper.getTemplate().getStringSerializer().serialize(key); 15 } 16 17 18 @Override 19 public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException { 20 //开启监听,一旦当前key发生变化,事务将被中断,将不会执行返回null 21 redisConnection.watch(key); 22 //若key存在,则获取不到锁 23 byte[] value= redisConnection.get(key); 24 if(value!=null){ 25 return false; 26 } 27 //事务块开始 28 redisConnection.multi(); 29 redisConnection.setEx(key,expire,new byte[]{'Y'}); 30 redisConnection.get(key); 31 //事务块执行,其返回值为执行事务块呢内所有命令的返回值,也就是上边为何加一条get(key),用于判断执行结果 32 List<Object> obj = redisConnection.exec(); 33 if(CollectionUtils.isNotEmpty(obj)&&obj.size()==1){ 34 //成功则获取该锁 35 return true; 36 } 37 return false; 38 } 39 }