基于redis集群实现的分布式锁,可用于秒杀,定时器。
在分布式系统中,经常会出现需要竞争同一资源的情况,使用redis可以实现分布式锁。
前提:redis集群已经整合项目,并且可以直接注入JedisCluster使用:
@Autowired private JedisCluster jedisCluster;
1. 新建RedisLockManger分布式锁管理器,并且如上注入 JedisCluster :
package com.jarfk.util.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.JedisCluster; import javax.annotation.PostConstruct; import java.util.concurrent.TimeUnit; /** * redis集群分布式锁管理器,支持对单个资源加锁解锁,或给一批资源的批量加锁及解锁 * Created by Administrator on 2017/10/12 0012. */ @Component public class RedisLockManger { private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockManger.class); //设置3秒过期 private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3; // private static final int DEFAULT_BATCH_EXPIRE_TIME = 6; //static的变量无法注解 @Autowired private JedisCluster jc; private static RedisLockManger lockManger; public RedisLockManger() { } @PostConstruct private void init() { lockManger = this; lockManger.jc = this.jc; } /** * 获取锁 如果锁可用 立即返回true, 否则立即返回false,作为非阻塞式锁使用 * @param key * @return */ public boolean tryLock(String key/* , String value*/) { try { return tryLock(key, key, 0L, null); } catch (InterruptedException e) { e.printStackTrace(); } return false; } /** * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false,作为阻塞式锁使用 * @param key 锁键 * @param value 被谁锁定 * @param timeout 尝试获取锁时长,建议传递500,结合实践单位,则可表示500毫秒 * @param unit,建议传递TimeUnit.MILLISECONDS * @return * @throws InterruptedException */ public boolean tryLock(String key , String value , long timeout , TimeUnit unit) throws InterruptedException { //纳秒 long begin = System.nanoTime(); do { //LOGGER.debug("{}尝试获得{}的锁.", value, key); Long i = lockManger.jc.setnx(key, value); if (i == 1) { lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME); LOGGER.debug(value + "-成功获取{}的锁,设置锁过期时间为{}秒 ", key, DEFAULT_SINGLE_EXPIRE_TIME); return true; } else { // 存在锁 ,但可能获取不到,原因是获取的一刹那间 // String desc = lockManger.jc.get(key); // LOGGER.error("{}正被{}锁定.", key, desc); } if (timeout == 0) { break; } //在其睡眠的期间,锁可能被解,也可能又被他人占用,但会尝试继续获取锁直到指定的时间 Thread.sleep(100); } while ((System.nanoTime() - begin) < unit.toNanos(timeout)); //因超时没有获得锁 return false; } /** * 释放单个锁 * @param key 锁键 */ public void unLock(String key/*, String value*/) { lockManger.jc.del(key); LOGGER.debug("{}锁被{}释放 .", key, key); } }
2. 使用示例:
首先在需要加锁的地方注入分布式锁管理器:
@Autowired private RedisLockManger redisLock;
然后调用即可,如:
if (redisLock.tryLock("statusCheck")) { //此处代码是锁上的 logger.debug("-----------------------:10秒执行一次!每次只有一个程序运行"); //释放锁,正常情况下,此处代码要注释掉,以免锁被释放,需要释放时可以根据自己逻辑的需要 //redisLock.unLock("statusCheck"); }
首先注入需要的