话不多说,上代码:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.stereotype.Component; import java.util.Objects;import java.util.concurrent.TimeUnit; /** * Description: 通用Redis帮助类 * User: zhouzhou * Date: 2018-09-05 * Time: 15:39 */@Componentpublic class CommonRedisHelper { public static final String LOCK_PREFIX = "redis_lock"; public static final int LOCK_EXPIRE = 300; // ms @Autowired RedisTemplate redisTemplate; /** * 最终加强分布式锁 * * @param key key值 * @return 是否获取到 */ public boolean lock(String key){ String lock = LOCK_PREFIX + key; // 利用lambda表达式 return (Boolean) redisTemplate.execute((RedisCallback) connection -> { long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1; Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes()); if (acquire) { return true; } else { byte[] value = connection.get(lock.getBytes()); if (Objects.nonNull(value) && value.length > 0) { long expireTime = Long.parseLong(new String(value)); if (expireTime < System.currentTimeMillis()) { // 如果锁已经过期 byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes()); // 防止死锁 return Long.parseLong(new String(oldValue)) < System.currentTimeMillis(); } } } return false; }); } /** * 删除锁 * * @param key */ public void delete(String key) { redisTemplate.delete(key); } }
如何使用呢,导入工具类后:
明星出轨Service{ public List get明星出轨(String id){ List list = getRedis(id); if(list == null){ boolean flag = CommonRedisHelper.lock(id); if(flag){ list = findListBydB(id); redis.set(id).value(list); CommonRedisHelper.delete(id); }else{ Threed.sleep(5000); return this.get明星出轨(id); } } return list; }}
加锁成功执行完逻辑后, 必须解锁, 否则只能靠锁机制来解锁了不建议这么做。在web开发时,我们使用redis更多是使用redisTemplate,所以了解redisTemplate实现分布式锁势在必行。
PS:现在添加另一个更保持原子性的redis分布式锁的工具类
public class RedisTool { @Autowired RedisTemplate redisTemplate; private static final Long SUCCESS = 1L; /** * 获取锁 * @param lockKey * @param value * @param expireTime:单位-秒 * @return */ public boolean getLock(String lockKey, String value, int expireTime){ boolean ret = false; try{ String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end"; RedisScriptredisScript = new DefaultRedisScript<>(script, String.class); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime); if(SUCCESS.equals(result)){ return true; } }catch(Exception e){ } return ret; } /** * 释放锁 * @param lockKey * @param value * @return */ public boolean releaseLock(String lockKey, String value){ String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisScript redisScript = new DefaultRedisScript<>(script, String.class); Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value); if(SUCCESS.equals(result)) { return true; } return false; }}
在使用上有一些小不同,可以根据业务进行融合
明星出轨Service{ public List get明星出轨(String id){ List list = getRedis(id); if(list == null){ boolean flag = redisTool.getLock("lockKey_"+id,id,1000 * 60); if(flag){ list = findListBydB(id); redis.set(id).value(list); redisTool.releaseLock("lockKey_"+id,id); }else{ Threed.sleep(5000); return this.get明星出轨(id); } } return list; }}
PS:我们有发现一种新的分布式锁,这个方法也可以实现分布式锁
boolean flag = redisTemplate.opsForValue().setIfAbsent(key,value);